基于FluentValidation,Blazor与ASP.NET Core控制器的验证位置选型
可行,且是复用验证逻辑的最佳实践之一
你的思路完全正确——将核心业务验证规则集中在服务层依赖的xxxInput类上,通过代理验证让xxxModel和xxxDto复用这套逻辑,既符合DRY(Don't Repeat Yourself)原则,又能保证业务规则的一致性,是非常合理的实践方案。
具体实现步骤
1. 编写核心Input类验证器
先为xxxInput创建完整的FluentValidation验证器,把所有业务规则(必填校验、格式验证、逻辑约束等)都写在这里:
public class xxxInputValidator : AbstractValidator<xxxInput> { public xxxInputValidator() { RuleFor(x => x.RequiredField).NotEmpty().WithMessage("必填字段不能为空"); RuleFor(x => x.NumberField).InclusiveBetween(1, 100).WithMessage("数值必须在1-100之间"); // 其他核心业务验证规则... } }
2. 为Model和Dto创建代理验证器
分别为xxxModel和xxxDto编写验证器,通过AutoMapper将对象映射为xxxInput后,复用核心验证器的逻辑:
Blazor侧的xxxModel验证器
public class xxxModelValidator : AbstractValidator<xxxModel> { private readonly IMapper _mapper; private readonly xxxInputValidator _inputValidator; public xxxModelValidator(IMapper mapper, xxxInputValidator inputValidator) { _mapper = mapper; _inputValidator = inputValidator; RuleFor(model => model) .Custom((model, validationContext) => { // 将Model映射为Input类 var input = _mapper.Map<xxxInput>(model); // 复用核心验证逻辑 var validationResult = _inputValidator.Validate(input); // 将验证错误同步到当前Model的验证上下文 foreach (var error in validationResult.Errors) { // 若Model与Input字段名不一致,需手动映射字段名(比如Model拆分的字段对应Input的单个字段) var targetPropertyName = MapToModelPropertyName(error.PropertyName); validationContext.AddFailure(targetPropertyName, error.ErrorMessage); } }); } // 处理字段名映射的辅助方法(按需实现) private string MapToModelPropertyName(string inputPropertyName) { // 示例:Input的"FullName"对应Model的"FirstName"+"LastName",则返回对应字段或自定义错误位置 return inputPropertyName switch { "FullName" => "FirstName", // 或返回统一的错误标识,根据UI需求调整 _ => inputPropertyName }; } }
API控制器侧的xxxDto验证器
逻辑和Model验证器一致,只是处理的对象是Dto:
public class xxxDtoValidator : AbstractValidator<xxxDto> { private readonly IMapper _mapper; private readonly xxxInputValidator _inputValidator; public xxxDtoValidator(IMapper mapper, xxxInputValidator inputValidator) { _mapper = mapper; _inputValidator = inputValidator; RuleFor(dto => dto) .Custom((dto, validationContext) => { var input = _mapper.Map<xxxInput>(dto); var validationResult = _inputValidator.Validate(input); foreach (var error in validationResult.Errors) { // 同样处理字段名映射(如果Dto与Input字段名不同) var targetPropertyName = MapToDtoPropertyName(error.PropertyName); validationContext.AddFailure(targetPropertyName, error.ErrorMessage); } }); } private string MapToDtoPropertyName(string inputPropertyName) { // 按需实现字段名映射逻辑 return inputPropertyName; } }
3. 注册验证器
在ASP.NET Core的Program.cs中注册所有验证器,确保Blazor和控制器能自动触发验证:
builder.Services.AddFluentValidationAutoValidation() .AddFluentValidationClientsideAdapters() .AddValidatorsFromAssemblyContaining<xxxInputValidator>();
关键注意事项
- 字段名映射:如果Model/Dto与Input的字段结构不一致(比如Model拆分了字段),必须将验证错误的字段名映射到对应前端/API期望的字段,避免错误提示错位。
- 默认值处理:Blazor Model的默认值要确保映射到Input后符合业务规则,比如默认值不能违反必填或数值范围约束。
- 验证性能:映射和验证的开销极小,无需过度优化;若为超高频请求场景,可考虑缓存映射后的Input对象(需注意线程安全)。
总结
这种方案将核心业务规则集中维护在服务层的Input类,避免了在Model和Dto中重复编写验证逻辑,既保证了规则的一致性,又降低了维护成本,是当前场景下的最佳实践之一。
内容的提问来源于stack exchange,提问作者Rouss
相关产品推荐
相关产品推荐

