为所有控制器全局validationModelState.IsValid

在我的ASP.NET核心控制器中,我总是检查ModelState是否有效:

[HttpPost("[action]")] public async Task DoStuff([FromBody]DoStuffRequest request) { if (!ModelState.IsValid) { return BadRequest("invalid parameters"); } else { return Ok("some data")); } } 

有没有办法使用filter全局检查ModelState的有效性,所以我不必再次在每个控制器的每个API项目中执行此操作? 如果动作可以依赖于模型状态有效而不需要检查,那将是很好的:

 [HttpPost("[action]")] public async Task DoStuff([FromBody]DoStuffRequest request) { return Ok("some data")); } 

您可以使用ActionFilter 。 它不是全局的,但它将问题从您的方法体转移到属性中。 我意识到它并没有完全解决你的问题,但它可能总比没有好。

 public class ModelStateValidationActionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { var modelState = actionContext.ModelState; if (!modelState.IsValid) actionContext.Response = actionContext.Request .CreateErrorResponse(HttpStatusCode.BadRequest, modelState); } } 

在你的控制器中:

 [HttpPost] [ModelStateValidationActionFilter] public IHttpActionResult Post(object model) { } 

我相信你也可以在你的控制器上设置它。 我实际上没有尝试过,但据此,它可以工作。

 [ModelStateValidationActionFilter] public class MyApiController : ApiController { } 

编辑:

正如@Camilo Terevinto所说,Core有点不同。 如果你想使用Core,只需使用这个ActionFilter即可。

 public class ModelStateValidationActionFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext context) { var modelState = context.ModelState; if (!modelState.IsValid) context.Result = new ContentResult() { Content = "Modelstate not valid", StatusCode = 400 }; base.OnActionExecuting(context); } } 

到目前为止,现有的答案是ASP.NET Web API而不是ASP.NET Core 。 在ASP.NET Core中实际执行此操作的方法是:

 public class SampleActionFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { // do something before the action executes } public void OnActionExecuted(ActionExecutedContext context) { // do something after the action executes } } 

并且您可以在Startup.cs中全局注册此filter,因此这将在每次调用中执行,您不必在每个操作/控制器中重复它:

 options.Filters.Add(typeof(SampleActionFilter)); 

请参阅官方文档中的更多内容 。

对于ASP.NET Core 2.0,避免将属性单独应用于所有ControllersActions ;

定义filter:

 namespace Test { public sealed class ModelStateCheckFilter : IActionFilter { public void OnActionExecuted(ActionExecutedContext context) { } public void OnActionExecuting(ActionExecutingContext context) { if (!context.ModelState.IsValid) { context.Result = new BadRequestObjectResult(context.ModelState); } } } } 

然后在Startup.cs ,将其添加为filter:

  public void ConfigureServices(IServiceCollection services) { services.AddMvc(config => { config.Filters.Add(new ModelStateCheckFilter()); }); } 

作为后续内容:在ASP.NET Core 2.1中,有一个名为[ApiController]的控制器属性。 如果包含该属性,它会自动使用名为ModelStateInvalidFilter的内置ActionFilter ,这意味着永远不会到达任何检查ModelState的自定义操作filter,因为[ApiController]属性已经注册了自己的filter。

要禁止该行为,当前文档提供此选项:

 services.Configure(options => { options.SuppressModelStateInvalidFilter = true; // This is the setting }); 

使用HandleInvalidModelState

PM> Install-Package HandleInvalidModelState

 [HttpPost] [TypeFilter(typeof(HandleInvalidModelWithViewActionFilterAttribute))] public IHttpActionResult Post(object model) {} 

除了基本情况场景(返回带有无效模型的视图)包之外,还支持返回请求的Json和重定向 。

免责声明:该软件包的作者

对于async方法,filter必须实现IAsyncActionFilter 。 我把这个filter放在一起,其中:

  • 如果ModelState有效,则不执行任何操作

要么

  • 将Result设置为BadRequestObjectResult,并提供有关失败的详细信息
  • 使用更多详细信息记录ModelState

 public class ValidModelStateAsyncActionFilter : IAsyncActionFilter { // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/loggermessage?view=aspnetcore-2.1 private static readonly Action, Exception> ModelStateLoggerAction; private readonly ILogger logger; static ValidModelStateAsyncActionFilter() { ModelStateLoggerAction = LoggerMessage.Define>(LogLevel.Warning, new EventId(1, nameof(ValidModelStateAsyncActionFilter)), "{ModelState}"); } public ValidModelStateAsyncActionFilter(ILogger logger) { this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { if (context.ModelState.IsValid) await next(); this.LogModelState(context); context.Result = new BadRequestObjectResult(GetErrorResponse(context)); } private static ErrorResponse GetErrorResponse(ActionContext context) { return new ErrorResponse { ErrorType = ErrorTypeEnum.ValidationError, Message = "The input parematers are invalid.", Errors = context.ModelState.Values.SelectMany(x => x.Errors) .Select(x => x.ErrorMessage) .Where(x => !string.IsNullOrEmpty(x)) .ToList() }; } private void LogModelState(ActionContext context) { // credit: https://blogs.msdn.microsoft.com/mazhou/2017/05/26/c-7-series-part-1-value-tuples/ var items = from ms in context.ModelState where ms.Value.Errors.Any() let fieldKey = ms.Key let errors = ms.Value.Errors from error in errors select (Key: fieldKey, ErrorMessage: error.ErrorMessage, ExceptionMessage: error.Exception.Message); ModelStateLoggerAction(this.logger, items.ToList(), null); } }