15 votes

Aspnet Core Decimal binding ne fonctionne pas sur les cultures non anglaises

J'ai une application aspnet core qui fonctionne avec une configuration non anglaise (espagnole) :

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")
            }
        });

        .........
    }

En anglais, la partie décimale d'un nombre décimal est délimitée par un point, alors qu'en espagnol, on utilise une virgule :

  • 10256.35 anglais
  • 10256,35 espagnol

J'ai placé cette action dans un contrôleur :

 [HttpPost]
 public decimal Test(decimal val)
 {
     return val;
 }

Si j'utilise postman et que j'envoie à cette action un json comme celui-ci {val : 15.30}, alors val dans l'action reçoit un 0 (le binding ne fonctionne pas à cause de la culture). Si j'envoie un json comme ceci {val : 15,30} alors dans l'action je reçois 15.30. Le problème que j'ai est que j'ai besoin que l'action accepte les décimales avec des virgules, parce que c'est le format qui vient des entrées de type texte dans les formulaires de l'application. Mais j'ai aussi besoin d'accepter les décimales avec un point qui vient de la requête au format json. Il n'y a aucun moyen de spécifier un décimal/float en json qui accepte une virgule (l'envoyer en tant que chaîne n'est pas une option). Comment faire ? Je suis en train de me rendre fou avec ça.

Merci !

22voto

Luc Gauthier Points 371

Apparemment, la liaison décimale dans ASP.NET core 1.0.0 n'est pas culture invariante par défaut. Le modèle de liaison dépend de la culture du serveur.

Vous pouvez modifier ce comportement avec une liaison de modèle personnalisée, comme le suggère Stephen Muecke. Voici le mien, basé sur Liaison de modèle personnalisée dans 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);
    }
}

Et dans Startup.cs :

services.AddMvc(config =>
{
    config.ModelBinderProviders.Insert(0, new InvariantDecimalModelBinderProvider());
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X