2017-09-11 2 views
1

Ich möchte einige Rohdaten vor der Zuweisung zu Modelleigenschaften auf Rohdaten anwenden. Nämlich, Komma durch Punkt zu ersetzen, um beide Strings "324.32" und "324, 32" in Doppel zu konvertieren. Also schrieb ich dieses Modell BinderModelleigenschaftsbindungen mit [FromBody] -Attribut

public class MoneyModelBinder: IModelBinder 
    { 
     private readonly Type _modelType; 
     public MoneyModelBinder(Type modelType) 
     { 
      _modelType = modelType; 
     } 

     public Task BindModelAsync(ModelBindingContext bindingContext) 
     { 
      if (bindingContext == null) 
      { 
       throw new ArgumentNullException(nameof(bindingContext)); 
      } 

      string modelName = bindingContext.ModelName; 


      ValueProviderResult providerResult = bindingContext.ValueProvider.GetValue(modelName); 

      if (providerResult == ValueProviderResult.None) 
      { 
       return TaskCache.CompletedTask; 
      } 

      bindingContext.ModelState.SetModelValue(modelName, providerResult); 

      string value = providerResult.FirstValue; 

      if (string.IsNullOrEmpty(value)) 
      { 
       return TaskCache.CompletedTask; 
      } 

      value = value.Replace(",", "."); 

      object result; 
      if(_modelType == typeof(double)) 
      { 
       result = Convert.ToDouble(value, CultureInfo.InvariantCulture); 
      } 
      else if(_modelType == typeof(decimal)) 
      { 
       result = Convert.ToDecimal(value, CultureInfo.InvariantCulture); 
      } 
      else if(_modelType == typeof(float)) 
      { 
       result = Convert.ToSingle(value, CultureInfo.InvariantCulture); 
      } 
      else 
      { 
       throw new NotSupportedException($"binder doesn't implement this type {_modelType}"); 
      } 


      bindingContext.Result = ModelBindingResult.Success(result); 
      return TaskCache.CompletedTask; 
     } 

    } 

dann entsprechenden Anbieter

public class MoneyModelBinderProvider : IModelBinderProvider 
    { 
     public IModelBinder GetBinder(ModelBinderProviderContext context) 
     { 
      if (context == null) 
      { 
       throw new ArgumentNullException(nameof(context)); 
      } 

      if(context.Metadata?.ModelType == null) 
      { 
       return null; 
      } 


      if (context.Metadata.ModelType.In(typeof(double), typeof(decimal), typeof(float))) 
      { 
       return new MoneyModelBinder(context.Metadata.ModelType); 
      } 
      return null; 
     } 
    } 

und Registrierung es in Startup.cs

services.AddMvc(options => 
     { 
      options.ModelBinderProviders.Insert(0, new MoneyModelBinderProvider()); 

     }); 

aber ich bemerkte ein seltsames Verhalten oder vielleicht habe ich etwas verpasst. Wenn ich diese Art von Aktion verwenden

public class Model 
    { 
     public string Str { get; set; } 
     public double Number { get; set; } 
    } 


    [HttpPost] 
    public IActionResult Post(Model model) 
    { 

     return Ok("ok"); 
    } 

und Versorgungsparameter innerhalb alles Query-String funktioniert: als erster Anbieter für Modell genannt wird sich dann für jede Eigenschaft des Modells. Aber Wenn ich [FromBody] -Attribut und Versorgungsparameter von JSON verwende, wird Provider für Modell aufgerufen, aber nie für Eigenschaften dieses Modells aufgerufen. Aber warum? Wie kann ich Binder mit FromBody verwenden?

+0

Cant Kommentar zu diesem Thema Sie beschreiben, sondern als eine einfache Arbeit um Sie könnte ein View-Modell akzeptieren (mit diesen Eigenschaften als Zeichenfolgen) und verwenden Sie dann AutoMapper, um es in Ihrer Controller-Aktion Ihrem Entitätsmodell/dto zuzuordnen. – Steveland83

+0

Setzen Sie den Inhaltstyp-Header beim Buchen explizit? Nur weil es wie JSON aussieht, heißt das nicht, dass es als JSON interpretiert wird. –

Antwort

0

Ich habe eine Lösung gefunden. Wie es beschrieben here [FromBody] verhält sich anders im Vergleich zu anderen Wert Providern - es konvertiert komplexe Objekte alle gleichzeitig über JsonFormatters. Also zusätzlich zu den Modellbindern sollten wir eine separate Logik nur für FromBody schreiben. Und natürlich können wir einige Punkte bei json Verarbeitung fangen:

public class MoneyJsonConverter : JsonConverter 
{ 
    public override bool CanWrite => false; 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(double); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     string value = (reader.Value ?? "").Replace(" ", "").Replace(",", "."); 

     TypeConverter converter = TypeDescriptor.GetConverter(modelType); 
     object result = converter.ConvertFromInvariantString(value); 

     return result;; 
    } 
} 

und mit

 services.AddMvc(options => 
     { 
      options.ModelBinderProviders.Insert(0, new MoneyModelBinderProvider()); 

     }).AddJsonOptions(options => 
     {    
      options.SerializerSettings.Converters.Add(new MoneyJsonConverter()); 
     }); 
Verwandte Themen