【.NET Core 開發心得】Web API 資料驗證

在 WebAPI 世界中,一定會針對傳入資料進行驗證。本篇文章記錄我在 .NET Core 的兩種驗證方法的實作過程,一種是使用內建的 Model Binding Validation,另一種是使用 FluentValidation 進行驗證。

本文記錄兩種實作方法,並不會比較或說明那個好那個不好,請各位看完後自己思考看看,再決定要採用哪種方法。

另外這文章記錄我的實作(簡易版)過程,算是 101 等級的文章,並沒有太多理論或撞牆部分。

一、Model Binding 驗證設定

有在 .NET Framework 寫過 MVC 或 WebAPI 都知道,只要在想被驗證的 Model 中的 Property 上掛上 Attribute Tags 就可以了。

public class Person
{
    [Required]
    public int Id { get; set; }
    [MinLength(0)]
    [MaxLength(10)]
    public string Name { get; set; }
    [EmailAddress]
    public string Email { get; set; }
    [Range(18, 60)]
    public int Age { get; set; }
}

還有哪些 Attribute Tags?或者如何自訂驗證?可以參考微軟官方說明:模型驗證

這樣我們就完成驗證相關設定了。

接下來使用 ModelState.IsValid == false 就能進行驗證了。

 

二、FluentValidation 驗證設定

  1. 安裝 FluentValidation:
    Install-Package FluentValidation.AspNetCore
  2. Startup.cs 中的 ConfigureServices 設定:
    • .AddMVC() 後加上 .AddFluentValidation()using FluentValidation.AspNetCore)。
    • 依賴注入:services.AddTransient<IValidator<Person>, PersonValidator>(); 其中 PersonValidator 是撰寫被驗證模型 Person 的驗證規則的地方。
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddMvc()
              .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
              .AddFluentValidation();
      
          services.AddTransient<IValidator<Person>, PersonValidator>();
      }

       

  3. 實作模組 Person 與 PersonValidator:

    • PersonValidator 繼承 AbstractValidator<Person>,開始撰寫 RuleFor()

      public class Person 
      {
      	public int Id { get; set; }
      	public string Name { get; set; }
      	public string Email { get; set; }
      	public int Age { get; set; }
      	public DateTime Birthday { get; set; }
      }
      
      public class PersonValidator : AbstractValidator<Person> 
      {
      	public PersonValidator() 
          {
      		RuleFor(x => x.Id).NotNull();
      		RuleFor(x => x.Name).Length(0, 100);
      		RuleFor(x => x.Email).EmailAddress();
      		RuleFor(x => x.Age).InclusiveBetween(18, 99);
      		RuleFor(x => x.Birthday).LessThan(x => DateTime.Today);
      	}
      }

       

    • 詳細使用 FluentValidation 方式,請參考:[料理佳餚] 讓 FluentValidation 把參數的檢查條件口語化

  4. 一樣於 Controller 使用 ModelState.IsValid == false 就能進行驗證了。

  5. 個人覺得原廠的錯誤訊息滿清楚的,當然你也能針對每個欄位進行客製化錯誤訊息。

    {
      "errors": {
        "Age": [
          "'Age' 必須在 18 (包含)和 60 (包含)之間, 您輸入了 0。"
        ],
        "Email": [
          "'Email' 不是有效的電子郵件地址。"
        ],
        "Birthday": [
          "'Birthday' 必須小於 '2019/3/30 上午 12:00:00'。"
        ]
      },
      "title": "One or more validation errors occurred.",
      "status": 400,
      "traceId": "0HLLKVDDP3BM3:00000001"
    }

     

其實 FluentValidation 功能相當的多,而且可能會和 Model Binding Validation 相衝,更多詳細設定可以參考這篇(推薦)文章。

 

三、調整執行驗證方式

很多人會在各 Action 中加上以下程式碼做驗證:

if(! ModelState.IsValid) 
{ 
    return BadRequest(); // 可自製錯誤訊息回傳回去
}

如果每個 Action 都掛上去,就會變成重複的程式,所以我將這段執行驗證程式放到 Action Filter 中。

public class ValidateModelAttribute : Attribute, IAsyncActionFilter
{
    [ProducesResponseType(400)]
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        if (context.ModelState.IsValid == false)
        {
            context.Result = new BadRequestObjectResult(context); // 可自製錯誤訊息回傳回去
        }

        await next();
    }
}

以上程式,兩種驗證方法都適用。

 

四、結尾

兩種驗證方式選擇其一使用就好,另外配合 Action Filter,讓沒通過驗證的一開始就踢出去。

以上作法只是新手入門,網路上也有很多針對這兩種驗證的教學(包含自定義驗證),可能遇到各種奇怪問題,未來有空在寫寫撞牆心得。