向 MediatR 行为管道添加验证?

Add validation to a MediatR behavior pipeline?

我正在使用 ASP.NET Core、内置容器和支持 "behavior" pipelines:

的 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>))

我需要管道中的 FluentValidation 验证器。在 MediatR 2 中,validation pipeline was created thus:

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);
    }

}

现在如何为新版本执行此操作?如何设置要使用的验证器?

过程完全相同,您只需更改界面即可使用新的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();
    }
}

对于验证器,您应该在 built-in 容器中将所有验证器注册为 IValidator<TRequest>,以便将它们注入到行为中。如果你不想一一注册,我建议你看看Scrutor library这个自带汇编扫描功能的大佬。这样它会自己找到你的验证器。

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

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

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

只需在配置部分插入:

services.AddFluentValidation(new[] {typeof(GenerateInvoiceHandler).GetTypeInfo().Assembly});

GitHub

在新版本 (MediatR (>= 9.0.0)) 上,您可以这样做:

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, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        var context = new ValidationContext<TRequest>(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();
    }
}

记得在以前的版本如FluentApi 8.0或以下版本中添加var context = new ValidationContext<TRequest>(request);它使用这样的东西var context = new ValidationContext(request);

用于在 IServiceCollection 下注册 Asp.Net 核心,代码如下:

services.AddMediatR(Assembly.GetExecutingAssembly());
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

希望对您有所帮助!