添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
追风的手套  ·  一个用JSP做的日历·  2 周前    · 
爱喝酒的手电筒  ·  Kafka => ...·  3 月前    · 
呐喊的打火机  ·  地底下有書(第 ...·  11 月前    · 

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號: N231025402
出刊日期: 2023/10/18

Fluent Validation是一個相當流行、開放源碼的.NET程式庫,用於模型資料的驗證。在ASP.NET Core MVC與Razor Page網站應用程式之中,可以使用Fluent Validation程式庫來取代ASP.NET Core內建的資料標註(Data Annotations)功能,以程式碼建立簡單的驗證規則來檢查資料有效性。Fluent Validation程式庫的官方網站在:https://docs.fluentvalidation.net/。


在這篇文章中,我將介紹如何在ASP.NET Core Razor Page網站中使用Fluent Validations程式庫進行驗證。


資料標註(Data Annotations)


提到開發ASP.NET Core網站應用程式的資料模型驗證,第一個想到的就是ASP.NET Core內建的資料標註(Data Annotations)功能,只要在模型類別的屬性套用System.ComponentModel.DataAnnotations命名空間下的Attribute類別,就可以自動進行驗證。例如以下的「Book」模型類別程式碼,利用「Required」 Attribute要求驗證資料必須輸入;「MaxLength」 Attribute用來設定資料最大長度;「Range」Attribute設定資料範圍,可指定資料最小、最大值。

using System.ComponentModel.DataAnnotations;
namespace MyModels {
  public class Book {
    [Display( Name = "圖書編號" )]
    public int Id { get; set; }
    [Display( Name = "圖書名稱" )]
    [Required( ErrorMessage = "圖書名稱不可為空白" )]
    [MaxLength( 50, ErrorMessage = "長度不可超過 {1}" )]
    public string? Title { get; set; } = null!;
    [Display( Name = "價格" )]
    [Range( 1, int.MaxValue, ErrorMessage = "{0} 有效範圍在 {1} 與 {2} 之間" )]
    public int Price { get; set; }
    [Display( Name = "出版日期" )]
    [DataType( DataType.Date )]
    public DateTime PublishDate { get; set; }
    [Display( Name = "庫存" )]
    public bool InStock { get; set; }
    [Display( Name = "說明" )]
    [MaxLength( 50, ErrorMessage = "長度不可超過 {1}" )]
    public string? Description { get; set; }


DataAnnotations技術將資料驗證邏輯與資料模型(Data Model)緊密整合在一起,不符軟體鬆散耦合(loosely coupled)精神,同時也增加模型(Model)類別的複雜度。除了使用ASP.NET Core內建的DataAnnotations功能來進行驗證之外,有一個好用的程式庫──Fluent Validations,可以讓你定義強型別的驗證規則,讓你完全掌控驗證的動作,將驗證程式碼從模型類別中抽離出來,降低了應用程式的複雜度。

同時Fluent Validation程式庫是一個開放源碼的程式庫,可以整合到ASP.NET Core MVC、Razor Page與Blazor類型的專案,以及Web API服務之中使用。

 
安裝FluentValidation.AspNetCore套件

 
要在Razor Page網站中使用Fluent Validation需要在專案之中先安裝FluentValidation.AspNetCore套件。從Visual Studio 2022開發工具「Tools」-「NuGet Package Manager」-「Package Manager Console」開啟「Package Manager Console」視窗,然後在命令提示字元中輸入指令安裝「FluentValidation.AspNetCore」套件:

Install-Package FluentValidation.AspNetCore

 
註冊FluentValidation.AspNetCore套件

 
接著我們需要在「Program.cs」檔案中加入程式碼,註冊Fluent Validation服務,為了簡單起見,省略掉其它程式碼:

using FluentValidation;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddFluentValidationClientsideAdapters();
var app = builder.Build();

「AddFluentValidationAutoValidation」方法在應用程式中加入Fluent Validation功能;而「AddFluentValidationClientsideAdapters」方法則啟用用戶端驗證功能。

 
設計「Book」模型

 
參考以下的程式碼,定義一個「Book」模型包含以下屬性:

using System.ComponentModel.DataAnnotations;
namespace MyModels {
  public class Book {
    [Display( Name = "圖書編號" )]
    public int Id { get; set; }
    [Display( Name = "圖書名稱" )]
    public string? Title { get; set; } = null!;
    [Display( Name = "價格" )]
    public int Price { get; set; }
    [Display( Name = "出版日期" )]
    [DataType( DataType.Date )]
    public DateTime PublishDate { get; set; }
    [Display( Name = "庫存" )]
    public bool InStock { get; set; }
    [Display( Name = "說明" )]
    public string? Description { get; set; }


定義模型驗證類別

 
Fluent Validation提供許多內建的驗證器(Validator)來檢查資料的有效性,��細的清單可以參閱官網: https://docs.fluentvalidation.net/en/latest/built-in-validators.html

 
Fluent Validation可以使用Lamdba運算式來設定驗證規則,我們可以在Razor Page專案之中加入一個「BookValidator」類別來進行驗證,請參考以下程式碼:

using FluentValidation;
using MyModels;
namespace MyRazorWeb.Validator {
  public class BookValidator : AbstractValidator<Book> {
    public BookValidator() {
      RuleFor(x => x.Id)
        .NotNull();
      RuleFor(x => x.Title)
        .NotNull()
        .MaximumLength(50);
      RuleFor(x => x.Price)
        .NotNull()
        .InclusiveBetween(1, int.MaxValue);
      RuleFor(x => x.PublishDate)
        .NotNull();
      RuleFor(x => x.Description)
        .Length(0, 50);


「BookValidator」類別需繼承「AbstractValidator」類別,其中的指的就是想要驗證的���型類別。驗證的規則要定義在建構函式之中,只要叫用「RuleFor」方法傳入Lambda運算式指明想要驗證的屬性名稱即可。

 
註冊Fluent Validation服務

 
「BookValidator」類別需要在Razor Page網站中Program.cs檔案內註冊之後才能夠使用,請參考以下程式碼:

using FluentValidation;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddFluentValidationClientsideAdapters();
builder.Services.AddScoped<IValidator<Book>, BookValidator>();
var app = builder.Build();


除了叫用「AddScoped」方法註冊「BookValidator」之外,你也可以改用「AddValidatorsFromAssemblyContaining」方法來進行註冊,請參考以下程式碼:

using FluentValidation;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddFluentValidationClientsideAdapters();
builder.Services.AddValidatorsFromAssemblyContaining<BookValidator>();
var app = builder.Build();


在Razor Page專案中的Create頁面程式如下:

@page
@model MyRazorWeb.Pages.Books.CreateModel
  ViewData[ "Title" ] = " Create ";
<h1> Book Create </h1>
<div class="row">
  <div class="col-md-12">
    <form method="post">
      <div asp-validation-summary="All" class="text-danger"> </div>
      <div class="mb-3">
        <label asp-for="Book.Title" class="form-label"> </label>
        <input asp-for="Book.Title" class="form-control" />
        <span asp-validation-for="Book.Title" class="text-danger"> </span>
      <div class="mb-3">
        <label asp-for="Book.Price" class="form-label"> </label>
        <input asp-for="Book.Price" class="form-control" />
        <span asp-validation-for="Book.Price" class="text-danger"> </span>
      <div class="mb-3">
        <label asp-for="Book.PublishDate" class="form-label"> </label>
        <input asp-for="Book.PublishDate" class="form-control" />
        <span asp-validation-for="Book.PublishDate" class="text-danger"> </span>
      <div class="mb-3 form-check">
        <label class="form-check-label">
          <input class="form-check-input" asp-for="Book.InStock" />
          @Html.DisplayNameFor( model => model.Book.InStock )
        </label>
      <div class="mb-3">
        <label asp-for="Book.Description" class="form-label"> </label>
        <input asp-for="Book.Description" class="form-control" />
        <span asp-validation-for="Book.Description" class="text-danger"> </span>
      <div class="mb-3">
        <input type="submit" value="Save" class="btn btn-primary" />
    </form>
  <a asp-page="./List"> Back to List </a>

 
網頁執行時,故意不填資料到文字方塊,再按下「Save」按鈕,請參考下圖所示,錯誤訊息會自動出現在「asp-validation-summary」與「asp-validation-for」標記協助程式(Tag Helper)所在的位置:


圖 1:錯誤驗證測試。

 
若修改文字方塊中的值,讓「Title(圖書名稱)」文字方塊中的字串超過50個字;並將「Price(價格)」設為負數值,再按下「Save」按鈕,則錯誤訊息會自動出現在「asp-validation-summary」與「asp-validation-for」標記協助程式(Tag Helper)所在的位置,請參考下圖所示:


圖 2:顯示錯誤訊息。

 
自訂驗證錯誤訊息

 
若需要自訂驗證錯誤訊息,可叫用「WithMessage」方法,例如以下驗證「Title」屬性的程式碼,在「NotNull」方法之後叫用「WithMessage」方法設定錯誤訊息為「圖書名稱不可為空白」;在叫用「MaximumLength」方法之後,再串接叫用「WithMessage」方法,設定錯誤訊息為「長度不可超過 50」;依此類推:

using FluentValidation;
using MyModels;
namespace MyRazorWeb.Validator {
  public class BookValidator : AbstractValidator<Book> {
    public BookValidator() {
      RuleFor( x => x.Id )
        .NotNull( );
      RuleFor( x => x.Title )
        .NotNull( )
        .WithMessage( "圖書名稱不可為空白" )
        .MaximumLength( 50 )
        .WithMessage( "長度不可超過 50 " );
      RuleFor( x => x.Price )
        .NotNull( )
        .WithMessage( "價格不可為空白" )
        .InclusiveBetween( 1, int.MaxValue )
        .WithMessage( $"有效範圍在 1 與 {int.MaxValue} 之間" );
      RuleFor( x => x.PublishDate )
        .NotEmpty( )
        .WithMessage( "出版日期不可為空白" );
      RuleFor( x => x.Description )
        .Length( 0, 50 )
        .WithMessage( "長度不可超過 50 " );


驗證測試的執行結果,請參考下圖所示:

圖 3:驗證測試。

 
錯誤訊息內可包含一些參數取得要驗證的屬性名稱、屬性值、最大值、最小值等等,我們可以將上個範例程式碼變更如下:

using FluentValidation;
using MyModels;
namespace MyRazorWeb.Validator {
  public class BookValidator : AbstractValidator<Book> {
    public BookValidator() {
      RuleFor( x => x.Id )
        .NotNull( );
      RuleFor( x => x.Title )
        .NotNull( )
        .WithMessage( "{PropertyName} 不可為空白" )
        .MaximumLength( 50 )
        .WithMessage( "{PropertyName} 長度不可超過 {MaxLength} " );
      RuleFor( x => x.Price )
        .NotNull( )
        .WithMessage( "{PropertyName} 不可為空白" )
        .InclusiveBetween( 1, int.MaxValue )
        .WithMessage( "有效範圍在 {From} 與 {To} 之間" );
      RuleFor( x => x.PublishDate )
        .NotEmpty( )
        .WithMessage( "{PropertyName} 不可為空白" );
      RuleFor( x => x.Description )
        .Length( 0, 50 )
        .WithMessage( "長度不可超過 {MaxLength} " );

 
驗證測試的執行結果,請參考下圖所示:


圖 4:驗證測試。

 
在Web API進行驗證

 
要在Web API的控制器使用Fluent Validation進行驗證,並不需要特別寫叫用Fluent Validation套件的程式碼。由於已在Program.cs註冊Fluent Validation服務,驗證的功能會自動生效,只需要在Web API控制器中利用「ModelState」的「IsValid」屬性進行判斷,只要驗證成功,屬性的值就為「true」,若驗證失敗屬性的值就為「false」,例如以下「BooksController」程式碼:

using Microsoft.AspNetCore.Mvc;
using MyModels;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace MyRazorWeb.Controllers {
  [Route( "api/[controller]" )]
  [ApiController]
  public class BooksController : ControllerBase {
    public static List<Book> books = new List<Book>( ) {
   new Book() {
     Id = 1,
     Title = " Essential Programming Language ",
     Price = 250,
     PublishDate = new DateTime(2019, 1, 2),
     InStock = true,
     Description = "Essential Programming Language ",
   new Book() {
     Id = 2,
     Title = " Telling Arts ",
     Price = 245,
     PublishDate = new DateTime(2019, 4, 15),
     InStock = true,
     Description = " Telling Arts ",
   new Book() {
     Id = 3,
     Title = " Marvel ",
     Price = 150,
     PublishDate = new DateTime(2019, 2, 21),
     InStock = true,
     Description = " Marvel ",
   new Book() {
     Id = 4,
     Title = " The Beauty of Cook",
     Price = 450,
     PublishDate = new DateTime(2019, 12, 2),
     InStock = true,
     Description = " The Beauty of Cook ",
   new Book() {
     Id = 5,
     Title = " Learning how to Cook ",
     Price = 450,
     PublishDate = new DateTime(2020, 1, 20),
     InStock = true,
     Description = " Learning how to Cook  ",
    public BooksController() {
    // GET: api/<BooksController>
    [HttpGet]
    public ActionResult Get() {
      try {
        return Ok( books );
      catch ( Exception ) {
        return StatusCode( StatusCodes.Status500InternalServerError,
          "Error get books from Server !" );
    [HttpPost]
    public ActionResult<Book> Post( [FromBody] Book book ) {
      try {
        if ( book == null ) {
          return BadRequest( );
        if ( !ModelState.IsValid ) {
          return StatusCode( StatusCodes.Status400BadRequest, ModelState );
        book.Id = books.MaxBy( b => b.Id )!.Id + 1;
        books.Add( book );
        return CreatedAtAction( nameof( Post ), new { id = book.Id }, book );
      catch ( Exception ) {
        return StatusCode( StatusCodes.Status500InternalServerError,
          "Error insert book to database !" );


使用Swagger來測試Web API,當送出POST請求要新增一筆圖書資料時,若資料沒有驗證問題,便可自動新增:

"id": 0, "title": "Programming JavaScript", "price": 500, "publishDate": "2023-08-30", "inStock": true, "description": "Programming JavaScript"

 
驗證測試的執行結果,請參考下圖所示:

圖 5:使用Swagger測試驗證。

 
接著使用Swagger測試,送出GET請求查詢圖書資料,可看到新增的圖書,請參考下圖所示:


圖 6:使用Swagger測試驗證。

 
執行結果,請參考下圖所示:


圖 7:使用Swagger測試驗證。

 
若故意輸入有問題的資料如下,讓新增的動作產生例外錯誤:

"id": 0, "price": -100, "inStock": true, "description": "string"



驗證測試的執行結果,請參考下圖所示:

圖 8:驗證測試。

 
當送出POST請求要新增一筆圖書資料時,便會得到400號錯誤,「Response body」會列出所有有問題的屬性與在Fluent Validation自訂的錯誤訊息:

圖 9:驗證測試。

 
驗證測試的執行結果,請參考下圖所示:

圖 10:驗證測試。

 
我們可以明確地透過程式碼,建立「BookValidator」物件,叫用「Validate」方法進行驗證,修改控制器程式如下,從方法傳回的「ValidationResult」物件的「Errors」屬性,可取得包含詳細錯誤資訊的「ValidationFailure」物件。  

using Microsoft.AspNetCore.Mvc;
using MyModels;
using MyRazorWeb.Validator;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace MyRazorWeb.Controllers {
  [Route( "api/[controller]" )]
  [ApiController]
  public class BooksController : ControllerBase {
... //略
    [HttpPost]
    public ActionResult<Book> Post( [FromBody] Book book ) {
      try {
        if ( book == null ) {
          return BadRequest( );
        BookValidator validator = new BookValidator( );
        var result = validator.Validate( book );
        if ( !result.IsValid ) {
          return StatusCode( StatusCodes.Status400BadRequest, result.Errors);
        book.Id = books.MaxBy( b => b.Id )!.Id + 1;
        books.Add( book );
        return CreatedAtAction( nameof( Post ), new { id = book.Id }, book );
      catch ( Exception ) {
        return StatusCode( StatusCodes.Status500InternalServerError,
          "Error insert book to database !" );

 
使用Swagger測試,送出POST請求要新增一筆有問題的圖書資料:

"id": 0, "price": -100, "inStock": true, "description": "string"

執行結果請參考下圖所示

圖 11:驗證測試。

 
使用相依性插入

 
ASP.NET Core MVC與Razor Page類型的網站都支援相依性插入(DI),因此從控制器類別的建構函式之中可以直接取得在Program.cs的「IValidator」來進行驗證,不需要明確在程式中建立Validator實體,要達到這個目的,可修改控制器類別的程式碼如下:

using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using MyModels;
using MyRazorWeb.Validator;
using System;
using System.ComponentModel.DataAnnotations;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace MyRazorWeb.Controllers {
  [Route( "api/[controller]" )]
  [ApiController]
  public class BooksController : ControllerBase {
    public static List<Book> books = new List<Book>( ) {
   new Book() {
     Id = 1,
     Title = " Essential Programming Language ",
     Price = 250,
     PublishDate = new DateTime(2019, 1, 2),
     InStock = true,
     Description = "Essential Programming Language ",
   new Book() {
     Id = 2,
     Title = " Telling Arts ",
     Price = 245,
     PublishDate = new DateTime(2019, 4, 15),
     InStock = true,
     Description = " Telling Arts ",
   new Book() {
     Id = 3,
     Title = " Marvel ",
     Price = 150,
     PublishDate = new DateTime(2019, 2, 21),
     InStock = true,
     Description = " Marvel ",
   new Book() {
     Id = 4,
     Title = " The Beauty of Cook",
     Price = 450,
     PublishDate = new DateTime(2019, 12, 2),
     InStock = true,
     Description = " The Beauty of Cook ",
   new Book() {
     Id = 5,
     Title = " Learning how to Cook ",
     Price = 450,
     PublishDate = new DateTime(2020, 1, 20),
     InStock = true,
     Description = " Learning how to Cook  ",
    private IValidator<Book> _validator;
    public BooksController( IValidator<Book> validator ) {
      _validator = validator;
    // GET: api/<BooksController>
    [HttpGet]
    public ActionResult Get() {
      try {
        return Ok( books );
      catch ( Exception ) {
        return StatusCode( StatusCodes.Status500InternalServerError,
          "Error get books from Server !" );
    [HttpPost]
    public ActionResult<Book> Post( [FromBody] Book book ) {
      try {
        if ( book == null ) {
          return BadRequest( );
        var result = _validator.Validate( book );
        if ( !result.IsValid ) {
          return StatusCode( StatusCodes.Status400BadRequest, result.Errors );
        book.Id = books.MaxBy( b => b.Id )!.Id + 1;
        books.Add( book );
        return CreatedAtAction( nameof( Post ), new { id = book.Id }, book );
      catch ( Exception ) {
        return StatusCode( StatusCodes.Status500InternalServerError,
          "Error insert book to database !" );


自訂驗證規則

 
若Fluent Validation程式庫內建的驗證規則不敷使用,也允許自訂。例如想要自訂一個驗證規則檢查字串的內容只能包含文字,不可包含數字或特殊符號,參考程式如下,可以在「BookValidator」類別中加入一個「IsAllLetter」方法撰寫驗證邏輯,方法傳回布林值表明驗證結果。接著我們就可以叫用「RuleFor」方法指明驗證屬性之後,再串接叫用「Must」方法,傳入「IsAllLetter」方法名稱來進行檢查:

using FluentValidation;
using MyModels;
namespace MyRazorWeb.Validator {
  public class BookValidator : AbstractValidator<Book> {
    public BookValidator() {
      RuleFor( x => x.Id )
        .NotNull( );
      RuleFor( x => x.Title )
        .NotNull( )
        .WithMessage( "{PropertyName} 不可為空白" )
        .MaximumLength( 50 )
        .WithMessage( "{PropertyName} 長度不可超過 {MaxLength} " );
      RuleFor( x => x.Price )
        .NotNull( )
        .WithMessage( "{PropertyName} 不可為空白" )
        .InclusiveBetween( 1, int.MaxValue )
        .WithMessage( "有效範圍在 {From} 與 {To} 之間" );
      RuleFor( x => x.PublishDate )
        .NotEmpty( )
        .WithMessage( "{PropertyName} 不可為空白" );
      RuleFor( x => x.Description )
        .Length( 0, 50 )
        .WithMessage( "長度不可超過 {MaxLength} " )
        .Must( IsAllLetter )
        .WithMessage( "{PropertyName} 只能包含文字,不可包含數字或特殊符號" )
    private bool IsAllLetter( string s ) {
      return s.All( char.IsLetter );
    
330f08fa-ab4e-40df-b05c-df8a67271021|12|4.9

Tags:

.NET | .NET Magazine國際中文電子雜誌 | ASP.NET Razor Pages | 許薰尹Vivid Hsu

  • 十月 2024 (3)
  • 九月 2024 (2)
  • 八月 2024 (4)
  • 七月 2024 (4)
  • 六月 2024 (4)
  • 五月 2024 (3)
  • 四月 2024 (4)
  • 三月 2024 (2)
  • 二月 2024 (2)
  • 一月 2024 (2)
  • 十二月 2023 (4)
  • 十一月 2023 (5)
  • 十月 2023 (4)
  • 九月 2023 (3)
  • 八月 2023 (4)
  • 七月 2023 (3)
  • 六月 2023 (2)
  • 五月 2023 (5)
  • 四月 2023 (4)
  • 三月 2023 (4)
  • 二月 2023 (3)
  • 一月 2023 (3)
  • 十二月 2022 (3)
  • 十一月 2022 (5)
  • 十月 2022 (4)
  • 九月 2022 (2)
  • 八月 2022 (4)
  • 七月 2022 (4)
  • 六月 2022 (4)
  • 五月 2022 (4)
  • 四月 2022 (4)
  • 三月 2022 (5)
  • 二月 2022 (4)
  • 一月 2022 (5)
  • 十二月 2021 (3)
  • 十一月 2021 (5)
  • 十月 2021 (4)
  • 九月 2021 (5)
  • 八月 2021 (4)
  • 七月 2021 (4)
  • 六月 2021 (4)
  • 五月 2021 (5)
  • 四月 2021 (4)
  • 三月 2021 (6)
  • 二月 2021 (4)
  • 一月 2021 (5)
  • 十二月 2020 (4)
  • 十一月 2020 (5)
  • 十月 2020 (4)
  • 九月 2020 (5)
  • 八月 2020 (5)
  • 七月 2020 (5)
  • 六月 2020 (4)
  • 五月 2020 (4)
  • 四月 2020 (5)
  • 三月 2020 (4)
  • 二月 2020 (4)
  • 一月 2020 (4)
  • 十二月 2019 (4)
  • 十一月 2019 (3)
  • 十月 2019 (4)
  • 九月 2019 (4)
  • 八月 2019 (3)
  • 七月 2019 (4)
  • 六月 2019 (4)
  • 五月 2019 (5)
  • 四月 2019 (4)
  • 三月 2019 (2)
  • 二月 2019 (2)
  • 一月 2019 (2)
  • 十二月 2018 (2)
  • 十一月 2018 (2)
  • 十月 2018 (3)
  • 九月 2018 (2)
  • 八月 2018 (2)
  • 七月 2018 (2)
  • 六月 2018 (2)
  • 五月 2018 (3)
  • 四月 2018 (4)
  • 三月 2018 (4)
  • 二月 2018 (3)
  • 一月 2018 (4)
  • 十二月 2017 (2)
  • 十一月 2017 (3)
  • 十月 2017 (3)
  • 九月 2017 (3)
  • 八月 2017 (3)
  • 七月 2017 (3)
  • 六月 2017 (2)
  • 五月 2017 (3)
  • 四月 2017 (2)
  • 三月 2017 (3)
  • 二月 2017 (4)
  • 一月 2017 (3)
  • 十二月 2016 (2)
  • 十一月 2016 (3)
  • 十月 2016 (2)
  • 九月 2016 (2)
  • 八月 2016 (2)
  • 七月 2016 (2)
  • 六月 2016 (4)
  • 五月 2016 (2)
  • 四月 2016 (2)
  • 三月 2016 (3)
  • 二月 2016 (3)
  • 十二月 2015 (4)
  • 十一月 2015 (2)
  • 十月 2015 (3)
  • 九月 2015 (3)
  • 八月 2015 (2)
  • 七月 2015 (3)
  • 六月 2015 (2)
  • 五月 2015 (2)
  • 四月 2015 (3)
  • 三月 2015 (2)
  • 二月 2015 (2)
  • 一月 2015 (4)
  • 十二月 2014 (4)
  • 十一月 2014 (2)
  • 十月 2014 (2)
  • 九月 2014 (3)
  • 八月 2014 (3)
  • 七月 2014 (4)
  • 六月 2014 (3)
  • 五月 2014 (3)
  • 四月 2014 (3)
  • 三月 2014 (2)
  • 二月 2014 (3)
  • 一月 2014 (5)
  • 十二月 2013 (3)
  • 十一月 2013 (3)
  • 十月 2013 (3)
  • 九月 2013 (4)
  • 八月 2013 (4)
  • 七月 2013 (4)
  • 六月 2013 (3)
  • 五月 2013 (3)
  • 四月 2013 (3)
  • 三月 2013 (3)
  • 二月 2013 (4)
  • 一月 2013 (5)
  • 十二月 2012 (3)
  • 十一月 2012 (4)
  • 十月 2012 (4)
  • 九月 2012 (4)
  • 八月 2012 (4)
  • 七月 2012 (4)
  • 六月 2012 (4)
  • 五月 2012 (4)
  • 四月 2012 (5)
  • 三月 2012 (3)
  • 二月 2012 (4)
  • 一月 2012 (2)
  • 十二月 2011 (3)
  • 十一月 2011 (6)
  • 十月 2011 (3)
  • 九月 2011 (3)
  • 八月 2011 (3)
  • 七月 2011 (2)
  • 六月 2011 (4)
  • 五月 2011 (5)
  • 四月 2011 (3)
  • 三月 2011 (4)
  • 二月 2011 (2)
  • 一月 2011 (5)
  • 十二月 2010 (1)
  • 十一月 2010 (2)
  • 十月 2010 (2)
  •