ASP.NET Core Model Binding Error Messages Localization

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:

  1. 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.

  2. 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:

    Sample Image

    Keys which I used are like original messages, except the key for ValueMustNotBeNull which was the same as ValueIsInvalid, so I used Null value is invalid. for it.

  3. Configure Options - In ConfigureServices method, when adding Mvc, configure its options to set message accessors for ModelBindingMessageProvider:

    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 use SetValueIsInvalidAccessor()
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:

  1. Go to Solution Explorer
  2. Right click on project → AddASP.NET Folder → Choose App_GlobalResources
  3. Right click on App_GlobalResources → Choose Add New Item
  4. Choose Resource File and set the name to ErrorMessages.resx
  5. 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.


  1. 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 example fa-IR for Persian language
    and enter translation for those messages, for example for ErrorMessages.fa-IR.resx:

    • PropertyValueInvalid: مقدار '{0}' برای '{1}' معتبر نمی باشد.
    • PropertyValueRequired: وارد کردن مقدار الزامی است.
  2. 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>

Sample Image

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

  1. 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;
    }
    }
  2. 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);
    }
  3. 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



Leave a reply



Submit