添加验证到MediatR行为管道?

gro*_*kky 19 c# cqrs fluentvalidation mediatr asp.net-core

我正在使用内置容器ASP.NET Core和支持"行为"管道的 MediatR 3 :

public class MyRequest : IRequest<string>
{
    // ...
}

public class MyRequestHandler : IRequestHandler<MyRequest, string>
{
    public string Handle(MyRequest message)
    {
        return "Hello!";
    }
}

public class MyPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
    {
        var response = await next();
        return response;
    }
}

// in `Startup.ConfigureServices()`:
services.AddTransient(typeof(IPipelineBehavior<MyRequest,str??ing>), typeof(MyPipeline<MyRequest,string>))
Run Code Online (Sandbox Code Playgroud)

我需要在管道中使用FluentValidation验证器.在MediatR 2中,创建了一个验证管道:

public class ValidationPipeline<TRequest, TResponse>
    : IRequestHandler<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{

    public ValidationPipeline(IRequestHandler<TRequest, TResponse> inner, IEnumerable<IValidator<TRequest>> validators)
    {
        _inner = inner;
        _validators = validators;
    }

    public TResponse Handle(TRequest message)
    {
        var failures = _validators
            .Select(v => v.Validate(message))
            .SelectMany(result => result.Errors)
            .Where(f => f != null)
            .ToList();
        if (failures.Any())
            throw new ValidationException(failures);
        return _inner.Handle(request);
    }

}
Run Code Online (Sandbox Code Playgroud)

我现在如何为新版本做到这一点?如何设置使用哪个验证器?

Mic*_*iey 18

过程完全相同,您只需更改界面即可使用新IPipelineBehavior<TRequest, TResponse>界面.

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
    {
        var context = new ValidationContext(request);
        var failures = _validators
            .Select(v => v.Validate(context))
            .SelectMany(result => result.Errors)
            .Where(f => f != null)
            .ToList();

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

        return next();
    }
}
Run Code Online (Sandbox Code Playgroud)

对于验证器,您应该IValidator<TRequest>在内置容器中注册所有验证器,以便将它们注入到行为中.如果您不想逐个注册它们,我建议您查看带有程序集扫描功能的优秀Scrutor库.这样它就可以找到你的验证器.

此外,使用新系统,您不再使用装饰器模式,只需在容器中注册您的通用行为,MediatR将自动获取它.它可能看起来像:

var services = new ServiceCollection();
services.AddMediatR(typeof(Program));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
var provider = services.BuildServiceProvider();
Run Code Online (Sandbox Code Playgroud)

  • 尽管表述很简洁,但我还是倾向于同意维德勋爵的观点。我试图在管道中实现验证而不引发异常。从设计的角度来看,我无法认同业务验证失败应该导致异常的观点。实现起来很容易,但就是感觉不对。一定有更好的办法。我将考虑向 IResponse 添加属性,考虑到管道处理程序中的开放泛型类型,这可能需要强制转换、反射或两者都需要。 (3认同)
  • 例外不是免费的。这是一个糟糕的设计。 (2认同)
  • @RahulDass 是的。本文指出了它 https://medium.com/the-cloud-builders-guild/validation-without-exceptions-using-a-mediatr-pipeline-behavior-278f124836dc 祝一切顺利! (2认同)

Get*_*toX 6

我已将 .net 核心集成打包到 nuget 中,请随意使用:https ://www.nuget.org/packages/MediatR.Extensions.FluentValidation.AspNetCore

只需在配置部分插入:

services.AddFluentValidation(new[] {typeof(GenerateInvoiceHandler).GetTypeInfo().Assembly});
Run Code Online (Sandbox Code Playgroud)

GitHub