ASP.NET Core Model Binding Error Messages Localization
To customize framework model binding error messages, you need to set custom accessors for different error message accessors of ModelBindingMessageProvider
.
Example
Here you can download a full source code of what is described in this post. The repository contains example for ASP.NET Core 2.0 (VS 2017.3) and ASP.NET Core 1.1 (VS 2015):
- r-aghaei/AspNetCoreLocalizationSample
Also here you can see the example, live:
- aspnetcorelocalizationsample.azurewebsites.net
Default Error Messages
These are default error messages which the framework shows when model binding to a property fails:
MissingBindRequiredValueAccessor A value for the '{0}' property was not provided.
MissingKeyOrValueAccessor A value is required.
ValueMustNotBeNullAccessor The value '{0}' is invalid.
AttemptedValueIsInvalidAccessor The value '{0}' is not valid for {1}.
UnknownValueIsInvalidAccessor The supplied value is invalid for {0}.
ValueIsInvalidAccessor The value '{0}' is invalid.
ValueMustBeANumberAccessor The field {0} must be a number.
In addition to above messages, ASP.NET Core 2.0 have these messages as well:
MissingRequestBodyRequiredValueAccessor A non-empty request body is required.
NonPropertyAttemptedValueIsInvalidAccessor The value '{0}' is not valid.
NonPropertyUnknownValueIsInvalidAccessor The supplied value is invalid.
NonPropertyValueMustBeANumberAccessor The field must be a number.
Localize ASP.NET Core Model Binding Error Messages
To localize ASP.NET Core model binding error messages, follow these steps:
Create Resource File - Create a resource file under Resources folder in your solution and name the file ModelBindingMessages.fa.resx. The name can be anything else but we will use it to create a localizer. In the example, I used fa (Persian) culture.
Add Resource Keys - Open the resource file and add keys and values which you want to use for localizing error messages. I used keys and values like below image:
Keys which I used are like original messages, except the key for
ValueMustNotBeNull
which was the same asValueIsInvalid
, so I used Null value is invalid. for it.Configure Options - In
ConfigureServices
method, when addingMvc
, configure its options to set message accessors forModelBindingMessageProvider
:public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization(options => { options.ResourcesPath = "Resources"; });
services.AddMvc(options =>
{
var F = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
var L = F.Create("ModelBindingMessages", "AspNetCoreLocalizationSample");
options.ModelBindingMessageProvider.ValueIsInvalidAccessor =
(x) => L["The value '{0}' is invalid.", x];
options.ModelBindingMessageProvider.ValueMustBeANumberAccessor =
(x) => L["The field {0} must be a number.", x];
options.ModelBindingMessageProvider.MissingBindRequiredValueAccessor =
(x) => L["A value for the '{0}' property was not provided.", x];
options.ModelBindingMessageProvider.AttemptedValueIsInvalidAccessor =
(x, y) => L["The value '{0}' is not valid for {1}.", x, y];
options.ModelBindingMessageProvider.MissingKeyOrValueAccessor =
() => L["A value is required."];
options.ModelBindingMessageProvider.UnknownValueIsInvalidAccessor =
(x) => L["The supplied value is invalid for {0}.", x];
options.ModelBindingMessageProvider.ValueMustNotBeNullAccessor =
(x) => L["Null value is invalid.", x];
})
.AddDataAnnotationsLocalization()
.AddViewLocalization();
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]{new CultureInfo("en"), new CultureInfo("fa")};
options.DefaultRequestCulture = new RequestCulture("en", "en");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
}Also add this code at beginning of
Configure
method:var supportedCultures = new[] { new CultureInfo("en"), new CultureInfo("fa") };
app.UseRequestLocalization(new RequestLocalizationOptions()
{
DefaultRequestCulture = new RequestCulture(new CultureInfo("en")),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
});
Important Note for ASP.NET Core 2.0
In ASP.NET Core 2.0, model binding message provider properties has got
read only, but a setter method for each property has been added.For example, to set
ValueIsInvalidAccessor
, you should useSetValueIsInvalidAccessor()
method this way:options.ModelBindingMessageProvider.SetValueIsInvalidAccessor (
(x) => L["The value '{0}' is invalid.", x]);
ASP.NET Core Model Binding Error Messages Localization in ASP.NET CORE 2.0
I also ran into this. These setters were replaced with methods such as SetValueIsInvalidAccessor
, the change is described here: https://github.com/aspnet/Announcements/issues/240
ASP.NET MVC Localizing or Changing Default Model Binding Error Messages
When you enter an invalid value for a property, if model binder cannot bind that value to the property, the model binder sets an error message for that property. It's different from data-annotations model validation. It's in fact model binder validation error.
Localizing or Changing Default Model Binding Error Messages
Model binding error messages are different from model validation messages. To customize or localize them, you need to create a global resource and register it in Application_Start
for DefaultModelBinder.ResourceClassKey
.
To do so, follow these steps:
- Go to Solution Explorer
- Right click on project → Add → ASP.NET Folder → Choose App_GlobalResources
- Right click on App_GlobalResources → Choose Add New Item
- Choose Resource File and set the name to ErrorMessages.resx
- In the resource fields, add the following keys and values and save the file:
PropertyValueInvalid
:The value '{0}' is not valid for {1}.
PropertyValueRequired:
A value is required.
Note: If you want to just customize the messages, you don't need any language-specific resource, just write custom messages in the ErrorMessages.resx
and skip next step.
If you want localization, for each culture, copy the resource file and paste it in the same folder and rename it to ErrorMessages.xx-XX.resx. Instead of
xx-XX
use the culture identifier, for examplefa-IR
for Persian language
and enter translation for those messages, for example for ErrorMessages.fa-IR.resx:PropertyValueInvalid
:مقدار '{0}' برای '{1}' معتبر نمی باشد.
PropertyValueRequired:
وارد کردن مقدار الزامی است.
Open Global.asax and in
Application_Start
, paste the code:DefaultModelBinder.ResourceClassKey = "ErrorMessages";
ASP.NET CORE
For ASP.NET Core read this post: ASP.NET Core Model Binding Error Messages Localization.
Where I can find all the DataAnnotations error keys for localization?
'Please enter a valid number.' this format looks like a default jquery validation message.
How did you apply the validation?
You can also directly define the validation message in javascript,and then localize the
validation
message, as my following demo. And this is jquery validation documentation.
<script>
$(document).ready(function () {
$('#form1').validate({
rules: {
PremiseArea: { //rule the number and range
number: true,
range: (1, 100)
}
},
messages: {
PremiseArea: {
number:"@Localizer["Please enter a valid number."]", //localize the message
range: "@Localizer["Range Error"]"
}
}
});
});
</script>
How to override Json.Net model binding exception messages for localization purposes?
Is there any way to catch these Json deserialization errors and
redirect them to ModelBindingMessageProvider, so that my localized
messages would work?
No, model binding and json input are different, model binder is for FromForm
, and JsonInputFormatter is for FromBody
. They are following different way. You could not custom the error message from ModelBindingMessageProvider
.
For JSON
, you may implement your own JsonInputFormatter
and change the error message like
CustomJsonInputFormatter
public class CustomJsonInputFormatter : JsonInputFormatter
{
public CustomJsonInputFormatter(ILogger<CustomJsonInputFormatter> logger
, JsonSerializerSettings serializerSettings
, ArrayPool<char> charPool
, ObjectPoolProvider objectPoolProvider
, MvcOptions options
, MvcJsonOptions jsonOptions)
: base(logger, serializerSettings, charPool, objectPoolProvider, options, jsonOptions)
{
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var result = await base.ReadRequestBodyAsync(context);
foreach (var key in context.ModelState.Keys)
{
for (int i = 0; i < context.ModelState[key].Errors.Count; i++)
{
var error = context.ModelState[key].Errors[i];
context.ModelState[key].Errors.Add($"This is translated error { error.ErrorMessage }");
context.ModelState[key].Errors.Remove(error);
}
}
return result;
}
}Register
CustomJsonInputFormatter
services.AddMvc(options =>
{
var serviceProvider = services.BuildServiceProvider();
var customJsonInputFormatter = new CustomJsonInputFormatter(
serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<CustomJsonInputFormatter>(),
serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings,
serviceProvider.GetRequiredService<ArrayPool<char>>(),
serviceProvider.GetRequiredService<ObjectPoolProvider>(),
options,
serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value
);
options.InputFormatters.Insert(0, customJsonInputFormatter);
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}Register localized Service into
CustomJsonInputFormatter
to custom the error message.
Overriding ModelBindingMessageProvider error messages
The issue is that the InputFormatter
is throwing exception and the exception message is used for the modelstate entry. You can disable this in services.AddMvc().AddJsonOptions(options => options.AllowInputFormatterExceptionMessages = false;)
. This will add an empty string for the error message, which you can detect and then just display a generic message to user. I have not found a better way of doing this but this method will suffice for now.
Related Topics
Routing with Multiple Get Methods in ASP.NET Web API
Getting Hash of a List of Strings Regardless of Order
String.Replace() VS. Stringbuilder.Replace()
Should I Unsubscribe from Events
C# Native Host with Chrome Native Messaging
List Sort Based on Another List
How to Increase Executiontimeout for a Long-Running Query
System.Net.Http: Missing from Namespace? (Using .Net 4.5)
Viewing PDF in Windows Forms Using C#
How to Set Up Httpcontent for My Httpclient Postasync Second Parameter
How to Group Windows Form Radio Buttons
Async Call with Await in Httpclient Never Returns
How to Data Bind a List of Strings to a Listbox in Wpf/Wp7
How to Generate a Hashcode from a Byte Array in C#
Async Implementation of Ivalueconverter
Open Source Cad Drawing (Dwg) Library in C#
How to Resolve Service for Type While Attempting to Activate