ASP.NET Core 验证消息为何全部显示默认无效值提示?
结论
仅凭现有配置和模型代码,无法确定唯一原因。The value '' is invalid. 是框架可能生成的默认提示,不能由此断定所有验证特性都没有读取到塞尔维亚语资源。
先检查 ModelState 中保存的实际错误,再分别排查资源消息为空、模型绑定失败和页面显示异常的问题。重复调用 AddDataAnnotationsLocalization() 通常无法解决这些问题。
先确认默认消息从哪里产生
ASP.NET Core 显示字段验证错误时,如果某个 ModelError.ErrorMessage 为空,框架会调用 ValueIsInvalidAccessor 生成后备消息。因此,页面出现默认提示时,原始错误可能没有可显示的文本。ASP.NET Core 的 ValidationHelpers 源码中可以看到这一行为。
在处理表单提交的 Action 或 Razor Pages OnPost 中,重新显示页面之前加入以下临时诊断代码,通过断点查看结果:
var diagnostics = ModelState
.Select(entry => new
{
Field = entry.Key,
Errors = entry.Value!.Errors.Select(error => new
{
Message = error.ErrorMessage,
Exception = error.Exception
}).ToArray()
})
.Where(entry => entry.Errors.Length > 0)
.ToArray();
重点区分以下情况:
Message为空:检查资源值、自定义验证结果,以及是否有代码向ModelState添加空消息。Exception不为空:在开发环境查看异常及其内部异常,确认是否涉及类型转换或其他绑定问题。Message已经是正确的塞尔维亚语:继续检查 Razor 视图、客户端验证脚本,以及手动覆盖错误文本的代码。
这些诊断数据只用于开发排查,不要直接返回给浏览器。
单独检查资源消息是否有效
现有的 ErrorMessageResourceType 与 ErrorMessageResourceName 写法可以用于资源化验证消息。先绕过表单和客户端脚本,直接检查一个内置验证特性的消息:
using System.ComponentModel.DataAnnotations;
using System.Globalization;
// 放在处理当前请求的方法中,通过断点检查变量。
var uiCulture = CultureInfo.CurrentUICulture.Name;
var attribute = new RequiredAttribute
{
ErrorMessageResourceName = "field_required",
ErrorMessageResourceType = typeof(Resources.DataAnnotations)
};
var message = attribute.FormatErrorMessage("Email");
var hasMessage = !string.IsNullOrWhiteSpace(message);
如果 message 为空或只有空白,检查 DataAnnotations.sr.resx 中 field_required 的值,也检查回退资源 DataAnnotations.resx。资源键存在但值为空时,无法提供有效的验证提示。
如果调用抛出异常,根据异常检查资源类、资源属性名称、属性类型和消息格式。例如,field_required 必须对应资源类中可供该机制访问的静态字符串属性,资源文本中的 {0} 等格式占位符也必须正确。资源引用不合法可能直接抛出异常,不能一概解释成默认提示。ValidationAttribute 源码说明了资源属性查找和错误消息配置的处理方式。
这里使用的是强类型资源类。ResourcesPath = "Resources" 不能代替对该资源类、生成代码和嵌入资源的检查。
明确配置请求语言
SetDefaultCulture("sr") 设置默认语言。为了明确声明应用支持的语言,可以补充 SupportedCultures 和 SupportedUICultures:
builder.Services.AddLocalization(options =>
options.ResourcesPath = "Resources");
builder.Services.AddControllersWithViews()
.AddDataAnnotationsLocalization();
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseRequestLocalization(options =>
{
options.SetDefaultCulture("sr")
.AddSupportedCultures("sr")
.AddSupportedUICultures("sr");
});
// 保留项目已有的其他中间件和端点配置。
本地化中间件应在需要读取请求语言的处理逻辑之前执行。资源语言主要看 CultureInfo.CurrentUICulture,日期等格式处理还涉及 CultureInfo.CurrentCulture。应在发生问题的请求中检查它们的实际值。Microsoft 的请求语言配置文档介绍了默认语言、支持语言及请求语言提供程序的关系。
这项配置可以明确语言选择,但不能证明当前故障由它引起。
单独处理 BirthDate 的绑定问题
BirthDate 是不可空的 DateOnly。空字符串无法转换成有效日期时会产生模型绑定错误;[Required] 也无法把已经存在的值类型默认值视为 null。模型绑定错误与 DataAnnotations 验证消息属于不同环节。Microsoft 模型绑定文档说明了类型转换失败时的处理方式。
如果表单需要用 [Required] 表达“必须填写生日”,可以考虑:
[DisplayName("birthDate")]
[DataType(DataType.Date)]
[Required(
ErrorMessageResourceName = "field_required",
ErrorMessageResourceType = typeof(Resources.DataAnnotations))]
[Adult(
ErrorMessageResourceName = "birthDate_adult",
ErrorMessageResourceType = typeof(Resources.DataAnnotations))]
public DateOnly? BirthDate { get; set; }
前提是 Adult 支持 DateOnly?,并让空值交给 [Required] 处理。用户输入无法解析的日期时,仍可能产生绑定错误,需要另行处理。
这个改动只针对生日字段,不能解释所有字符串字段为什么也显示相同提示。
最后检查自定义验证与页面输出
题目没有提供 Alpha、Adult、表单视图和提交处理代码,还需要检查这些位置:
- 自定义验证特性生成失败消息时,应使用配置的资源消息。如果手动构造
ValidationResult,可以使用FormatErrorMessage(validationContext.DisplayName),避免返回空文本或固定英文。 - 检查是否有代码把错误重新写入
ModelState,导致原有消息丢失。 - 核对字段名与
asp-for、asp-validation-for是否对应。例如 Email 字段可以使用:
<input asp-for="Email" />
<span asp-validation-for="Email"></span>
如果使用了客户端验证,再检查生成的 HTML 中 data-val-required、data-val-email 等属性是否已包含正确消息。如果属性正确而页面提示错误,应重点排查客户端脚本。
如果最终确认需要翻译模型绑定的默认提示,应通过 ModelBindingMessageProvider 的相应 Set...Accessor 方法配置。它与 [Required] 等验证特性的资源消息是两套配置。直接翻译后备提示,无法修复资源文本为空或错误被覆盖的问题。相关 API 文档列出了各类消息对应的设置方法。
备注:内容仅供参考。
