在我的ASP.NET核心控制器中,我总是检查ModelState是否有效:
[HttpPost("[action]")]
public async Task<IActionResult> DoStuff([FromBody]DoStuffRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest("invalid parameters");
}
else
{
return Ok("some data"));
}
}
有没有办法使用过滤器全局检查ModelState的有效性,所以我不必再次在每个控制器的每个API项目中执行此操作?如果动作可以依赖于模型状态有效且不需要检查,那将是很好的:
[HttpPost("[action]")]
public async Task<IActionResult> DoStuff([FromBody]DoStuffRequest request)
{
return Ok("some data"));
}
答案 0 :(得分:8)
您可以使用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)
{
}
我相信你也可以在你的控制器上设置它。我还没有真正尝试过,但according to this它可以奏效。
[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);
}
}
答案 1 :(得分:8)
到目前为止,现有答案适用于 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 中全局注册此过滤器,因此这将在每次调用中执行,您不必在每个操作/控制器中重复它:
options.Filters.Add(typeof(SampleActionFilter));
答案 2 :(得分:5)
作为后续操作:在ASP.NET Core 2.1中,有一个名为[ApiController]
的控制器属性。如果包含该属性,它将自动使用名为ActionFilter
的内置ModelStateInvalidFilter
,这意味着将永远无法访问检查ModelState的任何自定义操作过滤器,因为[ApiController]
属性已经注册了自己的过滤器
为抑制这种行为,当前文档提供了此选项:
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true; // This is the setting
});
答案 3 :(得分:3)
对于ASP.NET Core 2.0,要避免将属性单独应用于所有Controllers
或Actions
;
定义过滤器:
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
中,将其添加为过滤器:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(config =>
{
config.Filters.Add(new ModelStateCheckFilter());
});
}
答案 4 :(得分:1)
PM> Install-Package HandleInvalidModelState
示例
[HttpPost]
[TypeFilter(typeof(HandleInvalidModelWithViewActionFilterAttribute))]
public IHttpActionResult Post(object model)
{}
除了基本案例场景(返回带有无效模型的视图)包之外,还支持返回请求的Json和Redirection。
免责声明:软件包的作者
答案 5 :(得分:0)
对于async
方法,过滤器必须实现IAsyncActionFilter
。我把这个过滤器放在一起,其中:
OR
public class ValidModelStateAsyncActionFilter : IAsyncActionFilter
{
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/loggermessage?view=aspnetcore-2.1
private static readonly Action<ILogger, IList<(string Key, string ErrorMessage, string ExceptionMessage)>, Exception> ModelStateLoggerAction;
private readonly ILogger<ValidModelStateAsyncActionFilter> logger;
static ValidModelStateAsyncActionFilter()
{
ModelStateLoggerAction = LoggerMessage.Define<IList<(string Key, string ErrorMessage, string ExceptionMessage)>>(LogLevel.Warning, new EventId(1, nameof(ValidModelStateAsyncActionFilter)), "{ModelState}");
}
public ValidModelStateAsyncActionFilter(ILogger<ValidModelStateAsyncActionFilter> 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);
}
}