如何避免在每个PostBack上调用ModelState.IsValid?

时间:2018-02-16 17:42:47

标签: c# asp.net-core-mvc

我几乎总是想检查在回发时是否调用了ModelSate.IsValid。并且必须在每个帖子的开头检查都违反DRY原则,有没有办法让它自动检查?

示例:

[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);
}

3 个答案:

答案 0 :(得分:3)

框架提供了一个可以子类化的抽象ActionFilterAttribute

您可以使用操作过滤器自动验证模型状态,并在状态无效时返回任何错误:

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);
        }
    }
}

您可以将其用于个别操作或全局注册

参考Asp.Net Core : Action Filters

答案 1 :(得分:2)

您可以尝试这样的事情:

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.ModelState.IsValid)
        {
            filterContext.Result = new BadRequestResult();
        }
    }
}

您可以请求此filterContext.HttpContext.RequestServices.GetService<ILogger>()之类的任何注册服务。 您可以通过操作过滤您的操作或控制器:

[HttpPost("RegisterUser")]
[AllowAnonymous]
[ValidateModel]
public async Task<IActionResult> RegisterUser([FromBody] UserRegisterViewModel vmodel)
{
    ...
}

答案 2 :(得分:1)

我研究了这个并找到了我认为最好的答案。即使我实现了其他答案中提到的内容,我仍然需要在每个POST和PUT请求中添加[ValidateModel]属性来重复自己,这是我想要的东西为了避免,我还想记录模型无效的事情,其他答案并不是真的允许这样做。所以这是我的答案:

[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;
}

我不想在每个POSTPUT上添加[ValidateViewModel]来重复自己。所以我做了以下几点:

        services.AddMvc(config =>
        {
            config.Filters.Add(new ValidateViewModelAttribute());
        });

现在所有POSTPUT方法都已经过验证!!