向 MediatR 行为管道添加验证?
Posted
技术标签:
【中文标题】向 MediatR 行为管道添加验证?【英文标题】:Add validation to a MediatR behavior pipeline? 【发布时间】:2017-02-16 19:30:30 【问题描述】:我正在使用 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,string>), 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);
我现在如何为新版本执行此操作?如何设置使用哪个验证器?
【问题讨论】:
【参考方案1】:流程完全一样,只需要换个接口就可以使用新的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();
【讨论】:
我不明白的一件事(虽然我知道我可以注册一个特定的类型,因为你帮我弄清楚了另一个问题),为什么这些东西的所有示例都使用开放泛型进行注册?您肯定需要一个用于一组特定请求、处理程序等的管道,而不是为每个请求运行每个管道吗? 我想这取决于。例如,我很高兴将验证行为应用于所有请求。最坏的情况是,没有为这个特定的请求注册验证器,它变成了一个空操作,只是委托给下一个行为或实际的请求处理程序。保持它们的通用性也使注册更加简单。感谢您报告该问题,我从一些使用 Autofac 的代码中提取了该问题,ASP.NET Core 中的内置容器可能不支持数组。 例外不是免费的。这是一个糟糕的设计。 尽管表述简洁,但我倾向于同意维德勋爵的观点。我试图在我的管道中实现验证而不抛出异常。从设计的角度来看,我不能认同业务验证失败会导致异常的观点。这很容易实现,但感觉不对。一定有更好的办法。我将考虑向 IResponse 添加属性,考虑到管道处理程序中的开放泛型类型,这可能需要强制转换、反射或两者兼而有之。 @RahulDass 是的。这篇文章把它钉在了medium.com/the-cloud-builders-guild/… 一切顺利!【参考方案2】:我已将 .net 核心集成打包到 nuget 中,请随意使用: https://www.nuget.org/packages/MediatR.Extensions.FluentValidation.AspNetCore
只需在配置部分插入:
services.AddFluentValidation(new[] typeof(GenerateInvoiceHandler).GetTypeInfo().Assembly);
GitHub
【讨论】:
这是我最喜欢的答案。【参考方案3】:在新版本 (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 Core 的代码如下:
services.AddMediatR(Assembly.GetExecutingAssembly());
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
希望对您有所帮助!
【讨论】:
以上是关于向 MediatR 行为管道添加验证?的主要内容,如果未能解决你的问题,请参考以下文章