You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 07:02:45