【问题标题】:How to perform async ModelState validation with FluentValidation in Web API using .NET Core如何使用 .NET Core 在 Web API 中使用 FluentValidation 执行异步 ModelState 验证
【发布时间】:2019-03-07 15:59:45
【问题描述】:

这个问题是这个帖子的后续问题 - How to perform async ModelState validation with FluentValidation in Web API?。

我想知道 FluentValidation 是否有办法在 .net core web api 中执行异步 ModelState 验证。我有一个 FluentValidation Validator 类,其中包含异步验证方法,例如“MustAsync”,这意味着在我的业务服务类中,我使用“ValidateAsync”手动调用验证器。我还想使用同一个验证器类来验证来自请求的模型。我浏览了文档并阅读到这样做的唯一方法是手动调用“ValidateAsync()”方法,因为 .net 管道是同步的。我宁愿不必从我的控制器中手动调用此方法,我宁愿在启动时注册它(让框架自动调用我模型上的验证器)或用验证器装饰我的请求模型。

有没有人能够做到这一点?

谢谢!

【问题讨论】:

  • 链接的问题为“旧” ASP.NET 提供了一些示例代码。但是,您可以轻松地将其适应 ASP.NET Core,因为一般原则保持不变。你已经试过了吗?

标签: asp.net-core asp.net-core-webapi fluentvalidation


【解决方案1】:

基于链接的问题,我稍微调整了代码以与 ASP.NET Core(在我的例子中为 2.2)兼容。一般来说,这是使用IAsyncActionFilter 接口。你可以阅读它in the official docs。

public class ModelValidationActionFilter : IAsyncActionFilter
{
    private readonly IValidatorFactory _validatorFactory;
    public ModelValidationActionFilter(IValidatorFactory validatorFactory) => _validatorFactory = validatorFactory;

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        var allErrors = new Dictionary<string, object>();

        // Short-circuit if there's nothing to validate
        if (context.ActionArguments.Count == 0)
        {
            await next();
            return;
        }

        foreach (var (key, value) in context.ActionArguments)
        {
            // skip null values
            if (value == null)
                continue;

            var validator = _validatorFactory.GetValidator(value.GetType());

            // skip objects with no validators
            if (validator == null)
                continue;

            // validate
            var result = await validator.ValidateAsync(value);

            // if it's valid, continue
            if (result.IsValid) continue;

            // if there are errors, copy to the response dictonary
            var dict = new Dictionary<string, string>();

            foreach (var e in result.Errors)
                dict[e.PropertyName] = e.ErrorMessage;

            allErrors.Add(key, dict);
        }

        if (allErrors.Any())
        {
            // Do anything you want here, if the validation failed.
            // For example, you can set context.Result to a new BadRequestResult()
            // or implement the Post-Request-Get pattern.
        }
        else
            await next();
    }
}

如果您想全局应用此过滤器,您可以将过滤器添加到您的 Startup 类中的 AddMvc 调用中。例如:

 services.AddMvc(options => 
{
    options.Filters.Add<ModelValidationActionFilter>();

    // uncomment the following line, if you want to disable the regular validation
    // options.ModelValidatorProviders.Clear();
});

【讨论】:

    【解决方案2】:

    根据@nachtjasmin 上面的回答,您可以通过两种方式添加它,

    1. 使用 AddMvc

      services.AddControllersWithViews(options =>
      {
          options.Filters.Add<FluentValidationActionFilter>();
      });
      
    2. 使用 AddControllersWithViews

      services.AddControllersWithViews(options =>
      {
          options.Filters.Add<FluentValidationActionFilter>();
      });
      

    如果您的只是一个 Web API 并且您没有涉及任何 Razor 页面,那么您可以考虑使用 AddControllersWithViews 而不是 AddMvc,因为 AddMvc 在内部使用 AddControllersWithViews 并添加 @987654329 @ 最重要的是。

    你可以看到这个信息here for AddMvc和here for AddControllersWithViews

    【讨论】:

      【解决方案3】:

      我无法让@nachtjasmin 的答案中的代码与较新版本的 FluentValidation 一起使用。具体来说,问题在于ValidateAsync 现在采用IValidationContext 而不是正在验证的模型,并且在编译时不知道模型类型的情况下无法创建上下文。

      最后我偶然发现了this answer,它指出确切的类型并不重要,而是使用object。

      所以,而不是:

      var result = await validator.ValidateAsync(value);
      

      你可以使用:

      var context = new ValidationContext<object>(value);
      var result = await validator.ValidateAsync(context);
      

      【讨论】:

        猜你喜欢
        • 2016-01-21
        • 2019-02-09
        • 1970-01-01
        • 2018-08-09
        • 2019-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-09
        相关资源
        最近更新 更多