MediatR 管道中的双重验证

Double validation in MediatR pipeline

我在 ASP.NET Core 和新的 MediatR which supports pipelines. My pipeline

考虑这个动作:

[HttpPost]
[HandleInvalidCommand]
public IActionResult Foo(Command command)
{
    await _mediator.Send(command);
    return View();
}

因此,如果命令有效,则验证会发生两次(并且验证器对 运行 而言是昂贵的)。

我怎样才能最好地处理这个问题?

编辑:显而易见的方法是从管道中删除验证,但这并不好,因为命令可能来自 UI,但也来自应用程序本身。在这两种情况下您都需要验证。

FluentValidation 即使验证失败也不会停止处理您的命令 - 它只是注册规则。

Mediatr Validation Pipeline 检查现有的验证错误并停止发送命令 - 如果存在错误,处理程序将不会触发。

但是您实现了自己的逻辑 - HandleInvalidCommand。您应该选择 one 选项 - mediatr pipiline 或使用 ModelState.IsValid

实现自己的逻辑

我找到了另一种方法。也许不是最好的,但它确实有效。

定义这个接口

public interface IValidated
{
    bool AlreadyValidated { get; }
}

装饰请求

public class Command : IRequest, IValidated
{
    public bool AlreadyValidated { get; set; }
    // etc...
}

将请求的验证器更新为 use an interceptor:

public class CommandValidator : AbstractValidator<Command>, IValidatorInterceptor
{

    public CommandValidator() { 
        // validation rules etc.
    }

    public ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
    {
        return validationContext;
    }


    public ValidationResult AfterMvcValidation(ControllerContext controllerContext, ValidationContext validationContext, ValidationResult result)
    {
        var command = validationContext.InstanceToValidate as Command;
        if (command != null) command.AlreadyValidated = true;
        return result;
    }

}

更新管道:

public class MyPipeline<TRequest, TResponse>
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>, IValidated   // update here
{

    public async Task<TResponse> Handle(
        TRequest message,
        RequestHandlerDelegate<TResponse> next)
    {

        if (!message.AlreadyValidated)      // update here
        {
            var context = new ValidationContext(message);
            var failures = _validators
                .Select(v => v.Validate(context))
                .SelectMany(e => e.Errors)
                .Where(e => e != null)
                .ToList();

            if (failures.Count != 0)
                throw new ValidationException(failures);
        }

      return await next();
      }

}

所以在 MVC/FluentValidation 验证后,它设置了标志。然后在 CQRS 管道中,如果设置了该标志,则不会再次执行验证。

但是我不确定我是否喜欢这个,因为我正在将不应该存在的内容泄漏到命令中。

我认为理想的解决方案是将您在 command/query 和来自客户端的模型之间使用的 类 分开。这可能是最正确的设计,因为它使您的 command/query 类 专用于您的应用程序核心输入,因此不会被修改以适应客户并随时间变化。当涉及到您的应用程序核心时,这将使您的 CQRS 类 更加纯净。

然而,这确实意味着 类 的更多重复,以便为客户的输入提供更多 类。