How to avoid calling ModelState.IsValid on every PostBack?

一笑奈何 提交于 2019-12-01 23:21:43

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

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)
{
    ...
}

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!!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!