如何客製化 ASP.NET Core 的 Model Validation 預設錯誤訊息
TLDR
- ASP.NET Core 預設的 Model Validation 訊息僅提供英文,可透過建立資源檔 (.resx) 進行客製化。
- 驗證訊息分為「ModelBinding」與「ValidationMetadata」兩部分,需分別實作。
- 透過實作
IValidationMetadataProvider介面,可自動替換ValidationAttribute的預設錯誤訊息。 - 支援多國語系時,應將資源檔設為「沒有程式碼產生」,並透過
RequestLocalizationOptions設定SupportedUICultures。 - 設定語系時應區分
Culture(格式化)與UICulture(資源載入),避免混淆。
客製化 Model Validation 預設訊息
什麼情況下會遇到這個問題:當專案需要將 ASP.NET Core 內建的驗證錯誤訊息(如 RequiredAttribute 產生的 "The field is required.")轉換為中文或其他語言,且不希望手動為每個欄位設定錯誤訊息時。
ASP.NET Core 的驗證機制分為兩類:
- ModelBinding 驗證:與資料格式相關(如型別轉換失敗)。
- ValidationMetadata 驗證:與資料內容規則相關(如長度限制、必填)。
建立資源檔 (.resx)
首先建立資源檔存放對應的錯誤訊息。資源檔屬性設定如下:
- 建置動作:內嵌資源
- 複製到輸出目錄:不要複製
將所需的錯誤訊息鍵值對(Key-Value)填入資源檔中,例如 RequiredAttribute_ValidationError 對應 "{0} 欄位為必填。"。
建立客製化的 ValidationMetadataProvider
為了自動替換 ValidationAttribute 的錯誤訊息,需實作 IValidationMetadataProvider。
csharp
public class LocalizationValidationMetadataProvider : IValidationMetadataProvider {
private readonly ResourceManager resourceManager;
private readonly Type resourceType;
public LocalizationValidationMetadataProvider(Type type) {
resourceType = type;
resourceManager = new ResourceManager(type);
}
public void CreateValidationMetadata(ValidationMetadataProviderContext context) {
foreach (var attribute in context.ValidationMetadata.ValidatorMetadata.OfType<ValidationAttribute>()) {
if (attribute.ErrorMessageResourceName is null) {
bool hasErrorMessage = attribute.ErrorMessage != null;
if (hasErrorMessage) {
string? defaultErrorMessage = typeof(ValidationAttribute)
.GetField("_defaultErrorMessage", BindingFlags.NonPublic | BindingFlags.Instance)
?.GetValue(attribute) as string;
hasErrorMessage = attribute.ErrorMessage != defaultErrorMessage;
}
if (hasErrorMessage) {
continue;
}
string? name = GetMessageName(attribute);
if (name != null && resourceManager.GetString(name) != null) {
attribute.ErrorMessageResourceType = resourceType;
attribute.ErrorMessageResourceName = name;
attribute.ErrorMessage = null;
}
}
}
}
private string? GetMessageName(ValidationAttribute attr) {
switch (attr) {
case CompareAttribute _:
return "CompareAttribute_MustMatch";
case StringLengthAttribute vAttr:
if (vAttr.MinimumLength > 0) {
return "StringLengthAttribute_ValidationErrorIncludingMinimum";
}
return "StringLengthAttribute_ValidationError";
case DataTypeAttribute _:
return $"{attr.GetType().Name}_Invalid";
case ValidationAttribute _:
return $"{attr.GetType().Name}_ValidationError";
}
return null;
}
}註冊服務
在 Program.cs 中註冊上述 Provider 並設定 ModelBindingMessageProvider。
csharp
builder.Services.AddRazorPages()
.AddMvcOptions(options => {
var provider = options.ModelBindingMessageProvider;
provider.SetAttemptedValueIsInvalidAccessor((x, y) => string.Format(ModelBindingMessage.AttemptedValueIsInvalid, x, y));
// 其他 ModelBinding 訊息設定...
options.ModelMetadataDetailsProviders.Add(new LocalizationValidationMetadataProvider(typeof(ValidationMetadataMessage)));
});多國語系支援
什麼情況下會遇到這個問題:當應用程式需要根據使用者的語系設定,動態切換驗證錯誤訊息的語言時。
設定語系資源檔
建立對應語系的資源檔(如 ValidationMetadataMessage.zh-TW.resx),並將其存取修飾詞設為「沒有程式碼產生」。系統會自動根據 UICulture 讀取對應的資源檔。
設定 RequestLocalization
在 Program.cs 中配置 RequestLocalizationOptions。
csharp
WebApplication app = builder.Build();
string[] supportedCultures = new string[] { "zh-TW", "en-US" };
RequestLocalizationOptions localizationOptions = new RequestLocalizationOptions()
.SetDefaultCulture(supportedCultures[0])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);INFO
Culture 常見誤解
- DefaultRequestCulture 的運作機制:它是優先序最後的 Provider。系統會依序嘗試從 QueryString、Cookie 或 Accept-Language Header 尋找
UICulture,若找不到才會使用DefaultRequestCulture。 - Culture 與 UICulture 的區別:
Culture:控制日期、數值、貨幣的格式化與排序。UICulture:控制載入哪一種語言的資源檔。- 若透過 QueryString 設定語系,正確的參數名稱應為
ui-culture而非culture。
異動歷程
- 2022-10-05 初版文件建立。
- 2024-04-04 修正
ModelBindingMessage的訊息。
