如何避免在每次PostBack时调用ModelState.IsValid?

3

我几乎总是希望在提交后检查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);
}

2
研究操作过滤器 - Nkosi
@Nkosi 哦,我明白了,我也能将一个记录器注入到那个属性中吗? - NomenNescio
@Nkosi 不用管了,我想我可以使用这个:http://blog.ploeh.dk/2014/06/13/passive-attributes/ - NomenNescio
3个回答

6
该框架提供了一个抽象的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

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

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 方法都已经通过验证!!


1
这是准确的。不过我曾提到过,你可以将过滤器全局注册。即使是Get请求,isValid也会返回true,所以其实没有必要检查请求的Http方法。你也可以在全局添加过滤器时注入任何依赖项。无论哪种方式都可以。好答案。 - Nkosi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接