目录
在使用 MediatR 框架时,我们经常需要对命令对象进行验证。为了实现自动验证,我们可以使用 MediatR 的管道行为在命令处理之前执行验证逻辑。本文档将介绍如何在控制器外部使用 MediatR 的管道行为来自动验证实现了 ICommand/IRequest 接口的类,并提供正确地注册验证器和管道行为的方法。
使用Mediator来构建项目框架具有一系列的优点和缺点。以下是对这些优缺点的详细分析:
优点:
缺点:
ProductManage/
│
├── Controllers/ // 控制器文件夹
│ └── ProductController.cs
│
├── Models/ // 模型文件夹
│ └── Product.cs
│
├── Application/ // 应用层文件夹
│ ├── Commands/ // 命令文件夹
│ │ └── CreateProductCommand.cs
│ │
│ ├── Queries/ // 查询文件夹
│ │ └── GetProductQuery.cs
│ │
│ └── Handlers/ // 处理程序文件夹
│ ├── CreateProductCommandHandler.cs
│ └── GetProductQueryHandler.cs
│
├── Infrastructure/ // 基础设施层文件夹
│ └── Persistence/ // 持久化文件夹
│ └── DbContext.cs
│
├── Domain/ // 领域层文件夹
│ └── Entities/ // 实体文件夹
│ └── Product.cs
│
├── Validators/ // 验证器文件夹
│ └── CreateProductCommandValidator.cs
│
└── Startup.cs // 启动文件
编写一个管道行为来执行验证逻辑,这个管道行为将在命令处理之前执行验证。由于ICommand继承自IRequest接口,所以只需要添加IRequest管道拦截器
using FluentValidation;
using MediatR;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class ValidationQueryBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationQueryBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
// 执行所有验证器
var failures = _validators
.Select(v => v.Validate(request))
.SelectMany(result => result.Errors)
.Where(error => error != null)
.ToList();
var failuresAsync = _validators
.Select(async v => await v.ValidateAsync(request))
.SelectMany(result => result.Result.Errors)
.Where(error => error != null)
.ToList();
failures.AddRange(failuresAsync);
if (failures.Any())
{
throw new DecBussinessExpection(failures.FirstOrDefault().ErrorMessage, Convert.ToInt32(failures.FirstOrDefault().ErrorCode));
}
// 如果通过验证,则继续执行下一个处理程序
return await next();
}
}
接下来,我们需要将验证器和管道行为注册到 DI 容器中,以便在 MediatR 的管道中使用它们进行自动验证。
using FluentValidation.AspNetCore;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
var assemblies = Directory
.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "ProductManage.Application.*.dll", SearchOption.TopDirectoryOnly)
.Select(Assembly.LoadFrom).ToArray();
// 注册 MediatR
builder.Services.AddMediatR(assemblies);
// 注册 FluentValidation 验证器
builder.Services.AddValidatorsFromAssemblies(assemblies)
.AddFluentValidationAutoValidation()
.AddFluentValidationClientsideAdapters().AddMemoryCache().AddRuleEngine();
// 注册管道行为
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationQueryBehavior<,>));
}
}
我们需要定义一个实现了 ICommand 接口的命令类。这个命令类将在 MediatR 管道中被自动验证。
public class CreateProductCommand : ICommand<ProductDTO>
{
public string Name { get; set; }
public decimal Price { get; set; }
}
定义一个实现ICommandHandler接口的处理程序类,该类主要实现业务逻辑。
public class CreateProductHandler : ICommandHandler<CreateProductCommand, ProductDto>
{
public Task<ProductDto> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
//实现业务逻辑
}
}
然后,我们需要编写一个验证器来验证命令类。这个验证器将使用 FluentValidation 等验证库来定义验证规则。
using FluentValidation;
public class CreateProductCommandValidator : AbstractValidator<CreateProductCommand>
{
private IProductService _productService;
public CreateProductCommandValidator(IProductService productService)
{
_productService=productService;
}
public override async Task<ValidationResult> ValidateAsync(ValidationContext<CreateProductCommand> context, CancellationToken cancellation = default)
{
RuleFor(it => it.Name).NotNull().CustomException(CustomerErrorMsg.名称不能为空);
var command = context.InstanceToValidate;
if (await _decCustomerMainService.CheckData())
{
return new ValidationResult(new[] { new CustomValidationFailure(ProductErrorMsg.该商品不存在) });
}
return await base.ValidateAsync(context, cancellation);
}
}
[HttpPost()]
public async Task<ApiResponse<ProductDTO>> Create(CreateProductReq requestDto)
{
var productInfo= await _mediator.Send(new CreateProductCommand(requestDto));
return new ApiResponse<ProductDTO>() { Code = (int)ApiErrorCode.成功, Message = "成功", Result = productInfo};
}
通过以上步骤,我们成功地实现了在控制器外部使用 MediatR 的管道行为来自动验证实现了 ICommand 接口的类。首先,我们定义了命令类和命令验证器。然后,我们注册了验证器和管道行为到 DI 容器中,并编写了管道行为来执行验证逻辑。最终,我们可以在控制器中发送命令,而管道行为会自动执行验证逻辑,并在需要时抛出验证异常。
注:控制器方法参数,FluentValidation会自动拦截验证,不需要额外的在管道里配置拦截验证。
更多【microsoft-MediatR 框架使用FluentValidation对Comand/Query进行自动拦截验证】相关视频教程:www.yxfzedu.com