ModelMetadata的IsRequired未获取FluentValidation规则验证的技术问询
我之前也踩过这个坑!默认情况下,FluentValidation的验证规则和ASP.NET的ModelMetadata系统是完全独立的,所以你用NotEmpty()定义的必填规则,不会自动同步到ModelMetadata.IsRequired属性里,导致@Html.LabelFor(...)这类依赖该属性的辅助方法无法识别必填标记。下面给你几个实用的解决办法:
方法1:手动通过IMetadataAware同步规则(适合简单场景)
如果你的模型结构简单、规则变动少,可以让模型实现IMetadataAware接口,手动标记属性为必填:
[Validator(typeof(RegisterValidator))] public class RegisterModel : IMetadataAware { public string UserName { get; set; } public void OnMetadataCreated(ModelMetadata metadata) { // 找到UserName对应的元数据,标记为必填 var userNameMetadata = metadata.Properties.First(p => p.PropertyName == nameof(UserName)); userNameMetadata.IsRequired = true; } }
缺点是验证规则变更时,这里的代码也要同步修改,不够灵活。
方法2:自定义ModelMetadataProvider(推荐,一劳永逸)
这个方法能自动扫描所有FluentValidation的规则,把NotEmpty()/NotNull()这类必填规则映射到ModelMetadata.IsRequired,通用性极强:
步骤1:实现自定义元数据提供器
public class FluentValidationMetadataProvider : DataAnnotationsModelMetadataProvider { private readonly IValidatorFactory _validatorFactory; public FluentValidationMetadataProvider(IValidatorFactory validatorFactory) { _validatorFactory = validatorFactory; } protected override ModelMetadata CreateMetadata( IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { // 先调用默认逻辑生成元数据 var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); // 只处理属性级别的元数据 if (!string.IsNullOrEmpty(propertyName) && containerType != null) { // 获取当前模型的验证器 var validator = _validatorFactory.GetValidator(containerType); if (validator == null) return metadata; // 获取该属性的所有验证规则 var ruleDescriptor = validator.CreateDescriptor(); var propertyRules = ruleDescriptor.GetRulesForProperty(propertyName); // 判断是否存在NotEmpty或NotNull规则,设置IsRequired metadata.IsRequired = propertyRules.Any(rule => rule is INotNullRule || rule is INotEmptyRule); } return metadata; } }
步骤2:注册自定义提供器
在ASP.NET Core的Program.cs(或Startup.cs)里,把自定义提供器注册到服务中:
builder.Services.AddSingleton<IModelMetadataProvider, FluentValidationMetadataProvider>();
这样系统就会自动用我们的自定义提供器生成元数据,无需修改模型或验证器代码。
方法3:临时方案——同时添加[Required]特性
如果只是临时解决问题,可以在模型属性上同时加上ASP.NET原生的[Required]特性:
[Validator(typeof(RegisterValidator))] public class RegisterModel { [Required(ErrorMessage = "Username obbligatorio")] public string UserName { get; set; } }
但这种方式会导致规则重复,后续维护验证规则时需要改两处,不推荐长期使用。
原理补充
FluentValidation是作为独立的验证框架存在的,默认不会主动修改ASP.NET的ModelMetadata。我们需要通过自定义元数据提供器,桥接两个系统的规则,让ModelMetadata.IsRequired能正确识别FluentValidation的必填约束。
内容的提问来源于stack exchange,提问作者Ssocial.it Web Agency

