Reputation: 441
I have an aspnet core app that runs with a non english configuration (spanish):
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
......
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(new CultureInfo("es-AR"))
,SupportedCultures = new List<CultureInfo>
{
new CultureInfo("es-AR")
}
,SupportedUICultures = new List<CultureInfo>
{
new CultureInfo("es")
}
});
.........
}
In english a decimal number has its decimal part delimited with a dot, but in spanish a comma is used:
I have this action in a controller:
[HttpPost]
public decimal Test(decimal val)
{
return val;
}
If I use postman and send to that action a json like this {val: 15.30}, then val in the action recives a 0 (binding not working because of the culture). If I send a json like this {val: 15,30} then in the action I recive 15.30 The problem I have is, I need the action to accept decimals with commas, because that is the format that comes from inputs type text in the app's forms. But i also need to accept decimal with a dot that comes from request in json format. There is no way to specify a decimal/float in json that accepts a comma (send it as string is not an option). How can I do this??? I'm driving my self crazy with this.
Thanks!!
Upvotes: 15
Views: 12104
Reputation: 581
Apparently, the decimal binding in ASP.NET core 1.0.0 is not culture invariant by default. The model binding depends on the server culture.
You can change this behavior with a custom model binding as suggested by Stephen Muecke. Here is mine based on Custom Model Binding in ASP.Net Core 1.0 (RTM)
public class InvariantDecimalModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (!context.Metadata.IsComplexType && (context.Metadata.ModelType == typeof(decimal) || context.Metadata.ModelType == typeof(decimal?)))
{
return new InvariantDecimalModelBinder(context.Metadata.ModelType);
}
return null;
}
}
public class InvariantDecimalModelBinder : IModelBinder
{
private readonly SimpleTypeModelBinder _baseBinder;
public InvariantDecimalModelBinder(Type modelType)
{
_baseBinder = new SimpleTypeModelBinder(modelType);
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != ValueProviderResult.None)
{
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
var valueAsString = valueProviderResult.FirstValue;
decimal result;
// Use invariant culture
if (decimal.TryParse(valueAsString, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out result))
{
bindingContext.Result = ModelBindingResult.Success(result);
return Task.CompletedTask;
}
}
// If we haven't handled it, then we'll let the base SimpleTypeModelBinder handle it
return _baseBinder.BindModelAsync(bindingContext);
}
}
And in Startup.cs:
services.AddMvc(config =>
{
config.ModelBinderProviders.Insert(0, new InvariantDecimalModelBinderProvider());
});
Upvotes: 22