1

I'm on ASP.NET Core and the new MediatR which supports pipelines. My pipeline includes validation.

Consider this action:

[HttpPost]
[HandleInvalidCommand]
public IActionResult Foo(Command command)
{
    await _mediator.Send(command);
    return View();
}
  • The command is validated (I'm using FluentValidation)
  • HandleInvalidCommand checks ModelState.IsValid, and if invalid then redirects to the view for the user to correct the data
  • Else the action runs
  • The command is sent into the pipeline
  • The pipeline validates the command, AGAIN

So if the command is valid, then validation occurs twice (and validators are expensive to run).

How best can I deal with this?

EDIT: The obvious way is to remove validation from the pipeline, but that is no good because the command may come from the UI, but also from the app itself. And you want validation in both cases.

4

3 回答 3

1

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

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

于 2019-05-08T06:58:38.627 回答
1

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

定义这个接口

public interface IValidated
{
    bool AlreadyValidated { get; }
}

装饰请求

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

更新请求的验证器以使用拦截器

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 管道中,如果设置了该标志,则不会再次执行验证。

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

于 2017-02-22T12:18:04.907 回答
1

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

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

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

于 2017-02-22T11:46:45.497 回答