Blazor中DataAnnotationsValidator生成RequiredAttribute错误信息及本地化方案咨询
一、DataAnnotationsValidator生成RequiredAttribute错误提示的原理
Blazor的DataAnnotationsValidator依赖.NET原生的System.ComponentModel.DataAnnotations验证体系:
- 默认情况下,
RequiredAttribute自带的错误消息模板是"{0}字段是必填项",其中{0}会被替换为属性的显示名称(优先取[Display(Name = "...")]的设置,没有的话用属性名)。 - 验证时,
DataAnnotationsValidator会为每个属性创建ValidationContext,通过这个上下文读取属性的元数据和注解配置,格式化错误消息后返回给UI显示。 - 如果
RequiredAttribute指定了ErrorMessageResourceName和ErrorMessageResourceType,则会从对应的资源文件中读取本地化消息,而非默认模板。
二、你之前的方法无效的原因
你用AddControllers配置ModelMetadataDetailsProviders的方式只对MVC/Razor Pages的控制器验证生效,Blazor的表单验证是独立体系,不依赖MVC的控制器验证服务,所以这套配置无法影响Blazor的DataAnnotationsValidator。
三、可行的本地化解决方案
1. 自定义ValidationMessageProvider(灵活可控,推荐)
Blazor的DataAnnotationsValidator允许通过ValidationMessageProvider替换默认的消息生成逻辑,结合IStringLocalizer实现本地化:
步骤1:自定义ValidationMessageProvider
public class LocalizedValidationMessageProvider : ValidationMessageProvider { private readonly IStringLocalizer _localizer; public LocalizedValidationMessageProvider(IStringLocalizer localizer) { _localizer = localizer; } // 重写RequiredAttribute的错误消息生成方法 public override string GetRequiredErrorMessage(string fieldName) { // 从本地化资源中获取消息,{0}替换为字段名 return _localizer["RequiredField", fieldName]; } // 可按需重写其他注解的消息方法,比如GetStringLengthErrorMessage等 }
步骤2:注册服务
在Program.cs中注册自定义的ValidationMessageProvider和本地化服务:
// 注册本地化资源(假设资源文件存放在Resources文件夹下) builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); // 注册自定义ValidationMessageProvider builder.Services.AddSingleton<ValidationMessageProvider>(sp => { var localizer = sp.GetRequiredService<IStringLocalizer<SharedResources>>(); return new LocalizedValidationMessageProvider(localizer); });
步骤3:创建本地化资源文件
在项目中创建Resources文件夹,添加SharedResources.resx(默认语言)和SharedResources.zh-CN.resx(中文):
- 在
SharedResources.zh-CN.resx中添加键RequiredField,值为"{0}是必填项"
2. 直接给RequiredAttribute指定资源文件(原生DataAnnotations方式)
无需额外编写逻辑,直接在属性注解中指定本地化资源:
using System.ComponentModel.DataAnnotations; using YourProject.Resources; public class YourModel { [Display(Name = "UserName", ResourceType = typeof(SharedResources))] [Required(ErrorMessageResourceName = "RequiredField", ErrorMessageResourceType = typeof(SharedResources))] public string Name { get; set; } }
然后在SharedResources.resx和对应本地化文件中添加UserName(显示名称)和RequiredField(错误消息)的键值对即可。
3. 自定义ModelMetadataProvider(类似你之前的思路)
如果需要统一处理所有属性的显示名称和错误消息本地化,可以自定义IModelMetadataProvider:
public class LocalizedModelMetadataProvider : DefaultModelMetadataProvider { private readonly IStringLocalizer _localizer; public LocalizedModelMetadataProvider(IStringLocalizer localizer, IOptions<MvcOptions> options) : base(options) { _localizer = localizer; } protected override ModelMetadata CreateMetadata(ModelMetadataIdentity identity, ModelMetadata parent, Type modelType, string propertyName) { var metadata = base.CreateMetadata(identity, parent, modelType, propertyName); // 本地化显示名称 if (!string.IsNullOrEmpty(metadata.DisplayName)) { metadata.DisplayName = () => _localizer[metadata.DisplayName]; } return metadata; } }
然后在Program.cs中注册:
builder.Services.AddSingleton<IModelMetadataProvider, LocalizedModelMetadataProvider>();
这种方式可以统一处理所有属性的显示名称本地化,配合RequiredAttribute的资源配置一起使用效果更好。
内容的提问来源于stack exchange,提问作者SmRiley

