[.Net Core] 使用 InvalidModelStateResponseFactory 對 Model 檢核失敗時的 Response 進行加工

使用 InvalidModelStateResponseFactory 對 Model 檢核失敗時的 Response 進行加工

目標

在檢核 Request Model 失敗時,加上特定 Header 來回應前端。

 

 

現況

專案有使用 FluentValidation 針對比較複雜的情境作檢核,我們可以透過實作  IValidatorInterceptor 這個介面並註冊至 DI 容器後,當 FluentValidation 檢核發生錯誤時,會被這個 Interceptor 攔到,因此我們就可以簡單地加上我們想要的 Header 在回應中。

/// <summary>
/// 使用 FluentValidation 檢核時發生錯誤的攔截器
/// </summary>
public class ValidatorInterceptor : IValidatorInterceptor
{
    public ValidationResult AfterMvcValidation(ControllerContext controllerContext, IValidationContext commonContext, ValidationResult result)
    {
        if (!result.IsValid)
        {
            // FluentValidation 檢核失敗時補上特定錯誤碼於 x-error-code Header 中
            controllerContext.HttpContext.Response.Headers["X-ERROR-CODE"] = "Format.ModelInvalid";
        }

        return result;
    }

    public IValidationContext BeforeMvcValidation(ControllerContext controllerContext, IValidationContext commonContext)
    {
        return commonContext;
    }
}

 

當我們「僅」使用 ValidationAttribute 對 request model 做檢核時,在檢核失敗時並不會進入剛剛建立的 validatorInterceptor 中,此時要怎樣處理呢?

 

 

使用 InvalidModelStateResponseFactory 自訂檢核錯誤的回應

要完成這個任務,首先就是要有統一個地方攔到 Invalid Model 事件,而 ApiBehaviorOptions 提供一個 InvalidModelStateResponseFactory 方法來控制 Model 檢核錯誤時 Response 的資料結構,因此除了可以在這邊定義回應的資料,我們想要加註 error code header 的目標也可一併完成。

另外,由於筆者需求不想異動回應資料的結構,因此使用 ValidationProblemDetails 物件傳入 ModelState 資訊後就可以取得預設的資料結構,最後再以 BadRequestObjectResult 物件回應 HTTP Status 400 即可。

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

    services.Configure<ApiBehaviorOptions>(options =>
    {
        options.InvalidModelStateResponseFactory = actionContext =>
        {
            actionContext.HttpContext.Response.Headers["X-ERROR-CODE"] = "Format.ModelInvalid";
            
            var problemDetails = new ValidationProblemDetails(actionContext.ModelState);
            problemDetails.Type = options.ClientErrorMapping[400].Link;
            problemDetails.Title = "One or more validation errors occurred.";
            problemDetails.Status = StatusCodes.Status400BadRequest;
            problemDetails.Extensions["other"] = "add other property info here";  // 額外資訊

            return new BadRequestObjectResult(problemDetails);
        };
    });
}
如果想要以 HTTP Status 200 回應檢核錯誤的資訊時,可以回傳 OkObjectResult 物件。

 

結果如預期般地呈現所需 Header 資訊,並且保留原本預設的資料結構。打完收工!

 

 

 


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

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