[ASP.NET Core] 自訂 Invalid Model State 回應方式

開發 API 常會透過 ValidationAttribute 來規範 Request Model 屬性的特殊要求,預設當這些 Model 檢核不過時會以 HTTP Status 400 狀態回應,此時若團隊已有規範通用性的回應結構來傳遞所有訊息狀態時,就必須捨棄原本機制並自行定義所需的 Invalid Model State 回應方式。

前言


開發 API 常會透過 ValidationAttribute 來規範 Request Model 屬性的特殊要求,例如常見標籤 [Required], [MaxLength(max)], [RegularExpress(reg)] 分別表示必填、限制最大長度及使用正則表示式規範傳入字串的格式,預設當這些 Model 檢核不過時會以 HTTP Status 400 狀態回應,此時若團隊已有規範通用性的回應結構來傳遞所有訊息狀態時,就必須捨棄原本機制並自行定義所需的 Invalid Model State 回應方式。以下介紹。

 

 

使用版本


ASP.NET Core 2.1

 

 

停用 ModelStateInvalidFilter 


系統預設是透過 ModelStateInvalidFilter 來篩出 Model 驗證失敗的 Request 作錯誤回應,將該錯誤資訊收集後以 HTTP Status 400 連同錯誤資訊回應給呼叫端;因此我們可以透過 SuppressModelStateInvalidFilter = true 設定來停止這個 Filter 發生作用,成功設定後當 Model 驗證失敗則不會有錯誤回應。

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    // 停止 Invalid ModelState Filter (關閉後 Model 驗證失敗會無反應)
    services.Configure<ApiBehaviorOptions>(options =>
    {
      options.SuppressModelStateInvalidFilter = true;
    });
  }

  // 略 ...
}

 

 

建立自訂 ValidateModelFilter


由於預設的 Model 驗證錯誤處理機制已被關閉,因此當 Model 驗證發生錯誤時,我們就必須自行建立一個 Action Filter 來介入處理 Model 驗證失敗時的處置方式;以下列代碼為例,若團隊已經有制定通用性的 Respose 物件時,可以在錯誤發生時仍以 HTTP Status 200 正常回應,並透過此通用性物件來呈現錯誤資訊。

public class ValidateModelFilter : IActionFilter
{
  public void OnActionExecuting(ActionExecutingContext context)
  {
    if (!context.ModelState.IsValid)
    {
        // 取得 Model 錯誤資訊
        var modesState = context.ModelState;
        var errors = modesState.Keys
                        .SelectMany(key => modesState[key].Errors.Select(x => new { key, msg = x.ErrorMessage }))
                        .ToList();

        // 產生專案規範通用回應格式
        var result = new
        {
            header = new
            {
                isSuccess = false,
                code = "E0001",
                message = "Model Invalid"
            },
            body = new
            {
                errors
            }
        };

        // 回應結果
        context.Result = new JsonResult(result);
    }
  }

  // 略 ...
}

 

 

全域註冊 ValidateModelFilter


最後直接將剛才建立的 ValidateModelFilter 加入系統流程中就搞定了,請參考以下代碼。

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    // 略 ...

    // 加入自訂 Validate Model Filter 執行 Model 驗證失敗的處置
    services
      .AddMvc(options =>
      {
        options.Filters.Add(typeof(ValidateModelFilter));
        /* other filters ... */
      });
  }  
}

 

 

執行結果


假設登入系統 API 的 Request Body Model 定義如下,標示 [Required] 表示 Pcode 與 UserId 都是必填資訊。

 

當未填寫 UserId 與 Pcode 情境下送至 API 端時,會在 Model 繫結驗證時判定不合法,並在通過我們所定義的 ValidateModelFilter 被濾出,以我們所預期的通用性結構回應呼叫端,以下為測試時所呈現的結果,這樣就達到我們所預期的效果了。

 

 


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !