问题
I pretty much always want to check if ModelSate.IsValid
is called when I do a postback. And having to check at the start of every post back violates the DRY principle, is there a way to have it checked automatically?
Example:
[HttpPost("RegisterUser")]
[AllowAnonymous]
public async Task<IActionResult> RegisterUser([FromBody] UserRegisterViewModel vmodel)
{
if(!ModelState.IsValid) // This code is repeated at every postback
return ModelInvalidAction(); // Is there a way to avoid having to write it down?
// do other things
return StatusCode(201);
}
回答1:
The framework provides an abstract ActionFilterAttribute
that you can subclass.
You can use an action filter to automatically validate model state and return any errors if the state is invalid:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
You can either then use it on individual actions or register it globally
Reference Asp.Net Core : Action Filters
回答2:
You can try something like this:
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.ModelState.IsValid)
{
filterContext.Result = new BadRequestResult();
}
}
}
You can request any registered service like this filterContext.HttpContext.RequestServices.GetService<ILogger>()
.
You can decorate by action filter your action or controller:
[HttpPost("RegisterUser")]
[AllowAnonymous]
[ValidateModel]
public async Task<IActionResult> RegisterUser([FromBody] UserRegisterViewModel vmodel)
{
...
}
回答3:
I've researched this and found the best answer I think. Even if I implement what's mentioned in the other answers, I'll still be repeating myself by having to put a [ValidateModel]
attribute on each POST and PUT request, that's something I want to avoid, I would also like to log things if a model is invalid, other answers don't really allow for this. So here is my answer:
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class ValidateViewModelAttribute : Attribute, IFilterFactory
{
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var logger = serviceProvider.GetService<ILogger>();
return new InternalValidateModel(logger);
}
private class InternalValidateModel : IActionFilter
{
private ILogger _log;
public InternalValidateModel(ILogger log)
{
_log = log;
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (IsInvalidModelState(context))
{
_log.Information("Invalid ModelState: {Model}", context.ModelState.ErrorMessages());
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
private bool IsInvalidModelState(ActionExecutingContext context)
{
var method = context.HttpContext.Request.Method;
return (method == "POST" ||
method == "PUT") &&
!context.ModelState.IsValid;
}
}
public bool IsReusable => true;
}
I don't want to repeat myself by having to add a [ValidateViewModel] on every POST
and PUT
. So I do the following:
services.AddMvc(config =>
{
config.Filters.Add(new ValidateViewModelAttribute());
});
Now all POST
and PUT
methods are validated!!
来源:https://stackoverflow.com/questions/48832195/how-to-avoid-calling-modelstate-isvalid-on-every-postback