在 .NET Core API中,如果想在接口中验证某个模型,首先需要在该模型(类)类的属性上打上来自于`System.ComponentModel.DataAnnotations`命名空间下的特性。

```
    public class SomeModel
    {
        [Required(ErrorMessage = "电话号码必填")]
        [StringLength(30, ErrorMessage = "电话号码长度不能超过30")]
        [RegularExpression(@"^1[3458][0-9]{9}$", ErrorMessage = "手机号格式不正确")]
        public string Phone { get; set; }

        [Required(ErrorMessage = "姓名必填")]
        [StringLength(10, ErrorMessage = "姓名长度不能超过10")]
        public string Name { get; set; }
    }
```

然后在控制器方法中:

```
[HttpPost]
public async Task<IActionResult> SomeActionMethod([FromBody]SomeModel command){}
```

此时请求`SomeActionMethod`这个接口,如果`SomeModel`的字段值不符合验证定义,就会返回如下报错:

```
{
    "Name": [
        "姓名必填"
    ],
    "Phone": [
        "手机号格式不正确"
    ]
}

```
而在实际项目一般都有统一的返回格式,比如:

```
{
    "success": true,
    "message": "",
    "errors": {
        "name":[],
        "phone":[]
    }
}
```

**如何让API返回的模型验证失败信息符合自定义格式呢**?

# 错误的方式

在以前的 ASP .NET MVC或者 .NET Core 2.0时代我们可以通过继承`ActionFilterAttribute`这个基类来实现。

```
public class ValidateModelAttribute : ActionFiltereAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if(!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}
```

在`Startup.cs`中配置。

```
services.AddMvcCore(options => {
    options.Filters.Add(typeof(ValidateModelAttribute));
});
```

最后在控制器方法上套用。

```
[ValidateModel]
[HttpPost]
public async Task<IActionResult> SomeActionMethod([FromBody]SomeModel command){}
```

**很不幸**,在 .NET Core 2.2下,虽然以上写法没有报错,但实际是走不通的。

# 正确的方式

阅读官网后发现在 .NET Core 2.2下可以通过实现`IResultFilter`接口来实现。在这个接口的`OnResultExecuting`方法中返回自定义的格式。

> 在.NET Core 2.2 API中,当请求过来会来到请求管道,经过的路径大致包括:other middlewares → routing middleware  → action selection  → filter middlewares, 而在filter middleware中所经历的中间件依次是:authorization filter → resource filter → exception filter → model binding机制 → action filter  → result filter,而上面说的`IResultFilter`就属于result filter。
>
> 可以在一个类上增加多个过滤器。每个过滤器都有同步和异步方法,但不能同时使用同步或异步方法。如果既有同步方法也有异步方法会以异步方法优先。

言归正传。首先实现`IResultFilter`接口。

```
    /// <summary>
    /// 模型验证失败后返回的结果
    /// </summary>
    public class ValidateModelResultServiceFilter : IResultFilter
    {
        public void OnResultExecuted(ResultExecutedContext context)
        {                      
        }

        public void OnResultExecuting(ResultExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                context.Result = new ValidationFailedResult(context.ModelState);
            }
        }
    }
```

以上返回了自定义的`ValidationFailedResult`

```
public class ValidationFailedResult : ObjectResult
{
    public ValidationFailedResult(ModelStateDictionary modelState):base(new ValidationVM(modelState))
    {
        StatusCode = StatusCodes.Status422UnprocessableEntity;
    }
}
```

以上在`ValidationFailedResult`的构造函数中把`ValidationVM`实例交给了`ObjectResult`这个基类,`ObjectResult`负责把`ValidationVM`实例对象打印出来,即接口返回的格式。

```
   public class ValidationVM
    {
        /// <summary>
        /// 返回数据是否成功
        /// </summary>
        public bool Success { get; set; }
        /// <summary>
        /// 返回描述
        /// </summary>
        public string Message { get; set; }

        public List<VaidationError> Errors { get; }

        public ValidationVM(ModelStateDictionary modelState)
        {
            Success = false;
            Message = "输入模型无法通过验证";
            Errors = modelState.Keys.SelectMany(key => modelState[key].Errors.Select(x => new VaidationError(key, x.ErrorMessage))).ToList();
        }
    }

    public class VaidationError
    {
        [JsonProperty(NullValueHandling=NullValueHandling.Ignore)]
        public string Field { get; }
        public string Message { get; }

        public VaidationError(string field, string message)
        {
            Field = string.IsNullOrEmpty(field) ? null : field;
            Message = message;
        }
    }
```

需要在`Startup.cs`中放入DI容器。

```
services.AddScoped<ValidateModelResultServiceFilter>();
```

最后放置在某个控制器方法上。

```
[ServiceFilter(typeof(ValidateModelResultServiceFilter))]
[HttpPost] 
public async Task<IActionResult> SomeActionMethod([FromBody]SomeModel model){}
```