How to Customize Default Model Validation Error Messages in ASP.NET Core
TLDR
- ASP.NET Core's default Model Validation only provides English messages; these can be customized using resource files (.resx) and
IValidationMetadataProvider. - Validation messages are divided into "ModelBinding" and "ValidationMetadata" parts, which require separate implementation and configuration.
- The
ModelBindingMessageProvidercan be used to directly override ModelBinding error messages. - By implementing the
IValidationMetadataProviderinterface, you can dynamically replace the default error messages ofValidationAttribute. - To support multiple languages, create corresponding resource files and configure
SupportedUICulturesviaRequestLocalizationOptionsto enable automatic switching. - When setting the culture, distinguish between
Culture(formatting settings) andUICulture(resource file loading); the display of error messages depends onUICulture.
Handling ModelBinding and ValidationMetadata Validation Messages
In ASP.NET Core, the sources of Model Validation error messages are divided into two categories, handled as follows:
1. Create Resource Files (.resx)
Create resource files to store custom error messages, set the property to "Embedded Resource", and configure access modifiers as needed.
- ModelBindingMessage: Handles data format-related errors (e.g., type conversion failures).
- ValidationMetadataMessage: Handles data content validation errors (e.g.,
Required,Range, and other attributes).
2. Customize ValidationMetadataProvider
To replace the default error messages of ValidationAttribute, you must implement IValidationMetadataProvider.
When to use this: When you want to uniformly modify the default English error messages for all ValidationAttribute instances (such as RequiredAttribute) in your project without repeatedly writing ErrorMessage on every property.
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;
}
}3. Register Services
Register the provider above and configure ModelBindingMessageProvider in Program.cs.
builder.Services.AddRazorPages()
.AddMvcOptions(options => {
var provider = options.ModelBindingMessageProvider;
provider.SetAttemptedValueIsInvalidAccessor((x, y) => string.Format(ModelBindingMessage.AttemptedValueIsInvalid, x, y));
provider.SetMissingBindRequiredValueAccessor(x => string.Format(ModelBindingMessage.MissingBindRequiredValue, x));
// ... Other ModelBinding message settings
options.ModelMetadataDetailsProviders.Add(new LocalizationValidationMetadataProvider(typeof(ValidationMetadataMessage)));
});Implementing Multi-language Support
When to use this: When the application needs to dynamically display validation error messages in the corresponding language based on the user's language settings.
Configuration Steps
- Create resource files for the corresponding languages, such as
ModelBindingMessage.zh-TW.resx. - Set the resource file property to "Embedded Resource" and "Do not generate code".
- Configure
RequestLocalizationOptionsinProgram.cs.
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
Difference between Culture and UICulture
- Culture: Determines the formatting and sorting rules for dates, numbers, and currency.
- UICulture: Determines which language's resource file to load (error message display depends on this setting).
- If using QueryString to pass the language, the correct parameter should be
ui-culturerather thanculture.
Change Log
- 2022-10-05 Initial document creation.
- 2024-04-04 Fixed messages in
ModelBindingMessage.