添加验证到MediatR行为管道?

时间:2017-02-16 19:30:31

标签: c# asp.net-core cqrs fluentvalidation mediatr

我使用的是内置容器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);
    }

}

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

3 个答案:

答案 0 :(得分:15)

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

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

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

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

答案 1 :(得分:0)

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

只需插入配置部分:

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

GitHub

答案 2 :(得分:0)

在新版本 (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();
    }
}

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

用于在Asp.Net Core中的IServiceCollection下注册,代码如下:

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

希望对你有帮助!