2016-07-18 5 views
1

Ich muss Webhook-Endpunkt erstellen, die JSON-Nachrichten konsumieren wird.
Nachrichten werden als x-www-form-urlencoded in Form senden:WebAPI Post JSON Zeichenfolge und Zuordnung zu Modell

key = json
Wert = { "USER_ID": "728.409.840", "Call_ID": "1.114.330", "answered_time": "2015.04.16 15.37.47"}

wie in PostMan gezeigt:

enter image description here

Anfrage wie folgt aussieht:

json =% 7B% 22user_Id% 22% 3A +% 22728409840% 22% 2C +% 22call_id% 22% 3A +% 221.114.330% 22% 2C% 22answered_time% 22% 3A +% 222015-04-16 + 15% 3A37% 3A47% 22% 7D

Um Werte von Anfrage als meine Klasse (Modell) erhalten muss ich temporäre erstellen Objekt einzelne String-Eigenschaft enthält:

public class Tmp 
{ 
    public string json { get; set; } 
} 

und Verfahren in meinem Controller, der diese Anforderung verbraucht:

[AllowAnonymous] 
[Route("save_data")] 
[HttpPost] 
public IHttpActionResult SaveData(Tmp tmp) 
{ 
    JObject json2 = JObject.Parse(tmp.json); 
    var details = json2.ToObject<CallDetails>(); 
    Debug.WriteLine(details); 
    //data processing 
    return Content(HttpStatusCode.OK, "OK", new TextMediaTypeFormatter(), "text/plain"); 
} 

Wie Sie sehen können Tmp Klasse ist nutzlos.

Gibt es eine Möglichkeit Anforderungsdaten, da diese Klasse zu erhalten:

public class CallDetails 
{ 
    public string UserId { get; set; } 
    public string CallId { get; set; } 
    public string AnsweredTime { get; set; } 
} 

ich von IModelBinder Klasse bewusst bin, aber bevor ich, ich würde gerne wissen, beginnen, wenn es einen einfacheren Weg.

Ich kann Web-Anfrage-Format nicht ändern, nach Format ich meine, das ist immer POST mit einzelnen Schlüssel - JSON yhat hat JSON-Zeichenfolge als Wert.

+0

warum können Sie nicht ändern, die Inhaltstyp zu Anwendung/Json, wenn es Json ist, dass Sie herumgehen? –

+2

Ohne Bezug auf die Frage, aber ich bin gestört durch die Tatsache, dass Sie das 'async' Schlüsselwort verwenden, nur um' Task.Delay (1) ' –

+0

@MatiasCicero Ich habe Code für die Verarbeitung von Daten und das Speichern zu entfernen Datenbank, ich mache alles async, also habe ich statt der Methodendeklaration einfach 'Task.Delay (1) 'hinzugefügt, sorry, wenn es verwirrend ist. – Misiu

Antwort

0

Json.NET von NewtonSoft kann Ihnen helfen deserialize an object. Wenn Ihre json-Eigenschaftsnamen nicht mit Ihren tatsächlichen Klassennamen übereinstimmen, können Sie einen benutzerdefinierten Konverter erstellen, der Ihnen hilft.

EDIT

Sie könnten dies versuchen, wenn Sie auf MVC6 sind. Ändern Sie Ihre Parameter vom Typ TmpCallDetails eingeben und markieren Sie es mit dem Attribut [FromBody], wie folgt aus:

public IHttpActionResult SaveData([FromBody]CallDetails details) 

Zum Beispiel sehen Sie im Abschnitt „Verschiedene Modell Bindung“ in this post. Ich denke jedoch immer noch, dass Sie manuell deserialisieren müssen, da die Eigenschaftsnamen der Klasse CallDetails nicht genau mit den eingehenden JSON-Eigenschaften übereinstimmen.

+0

Ich habe einige weitere Details zu meiner Frage hinzugefügt. Im Grunde möchte ich die Verwendung der 'Tmp' Klasse entfernen und stattdessen eine Anfrage an Tmp und dann an CallDetails stellen. Ich möchte meine Anfrage von Anfang an CallDeails zuordnen. – Misiu

+0

Könnten Sie bitte etwas Code posten, und das würde viel helfen. Gibt es keine generische Lösung? Ich habe vier Methoden, die ein ähnliches Problem haben. Ich hätte gerne mehr wiederverwendbare Lösung. – Misiu

+0

MVC5 hat auch 'FromBody' Attribut, aber es hat nicht geholfen. Bei der Deserialisierung von XML können wir Attribute für Elementnamen verwenden. Kann ähnliches mit JSON gemacht werden? – Misiu

0

Sie vergessen nicht, die URL zu entschlüsseln, die vor der Verwendung von JObject.Parse codiert wurde, das funktioniert vielleicht.Und die Eigenschaften des Objekts dont die json atributes passen

+1

Ich habe einige weitere Details zu meiner Frage hinzugefügt. Im Grunde möchte ich die Verwendung der 'Tmp' Klasse entfernen und stattdessen eine Anfrage an Tmp und dann an CallDetails stellen. Ich möchte meine Anfrage von Anfang an CallDeails zuordnen. – Misiu

2

Sie JsonProperty Attribut für die Zuordnung von json Objekteigenschaften C# Objekteigenschaften verwenden können:

public class CallDetails 
{ 
    [JsonProperty("user_id")] 
    public string UserId { get; set; } 
    [JsonProperty("call_id")] 
    public string CallId { get; set; } 
    [JsonProperty("answered_time")] 
    public string AnsweredTime { get; set; } 
} 

Dann kann es ohne Temperaturklasse verwendet werden:

[AllowAnonymous] 
[Route("save_data")] 
[HttpPost] 
public IHttpActionResult SaveData(CallDetails callDetails) 

aktualisieren. Da die Daten als x-www-form-urlencoded gesendet werden, denke ich, dass die Art und Weise, wie Sie damit umgegangen sind, am einfachsten und nicht so schlimm ist. Wenn Sie eine andere Optionen hier überprüfen möchten, sind einige von ihnen:

Option 1 - benutzerdefinierte Modell Binder. Etwas wie dieses:

public class CustomModelBinder : IModelBinder 
{ 
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) 
    { 
     var body = actionContext.Request.Content.ReadAsStringAsync().Result; 
     body = body.Replace("json=", ""); 
     var json = HttpUtility.UrlDecode(body); 

     bindingContext.Model = JsonConvert.DeserializeObject<CallDetails>(json); 

     return true; 
    } 
} 

Und Verbrauch: SaveData([ModelBinder(typeof(CustomModelBinder))]CallDetails callDetails). Nachteil - Sie verlieren Validierung und vielleicht andere Dinge in Web-API-Pipeline definiert.

Option 2 - DelegatingHandler

public class NormalizeHandler : DelegatingHandler 
{ 
    public NormalizeHandler(HttpConfiguration httpConfiguration) 
    { 
     InnerHandler = new HttpControllerDispatcher(httpConfiguration); 
    } 

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     var source = await request.Content.ReadAsStringAsync(); 
     source = source.Replace("json=", ""); 
     source = HttpUtility.UrlDecode(source); 

     request.Content = new StringContent(source, Encoding.UTF8, "application/json"); 

     return await base.SendAsync(request, cancellationToken); 
    } 
} 

Verbrauch:

[AllowAnonymous] 
[HttpPost] 
public IHttpActionResult SaveData(CallDetails callDetails) 

Nachteil - Sie benötigen dafür individuelle Route definieren:

config.Routes.MapHttpRoute(
      name: "save_data", 
      routeTemplate: "save_data", 
      defaults: new { controller = "YourController", action = "SaveData" }, 
      constraints: null, 
      handler: new NormalizeHandler(config) 
     ); 
+0

Vielen Dank für die Antwort, aber was ist mit Anfrage Format? Daten werden mit POST mit key = 'JSON' und Wert =' {"user_Id": "728409840", "call_id": "1114330", "answered_time": "2015-04-16 15:37:47"} 'gesendet .Ich möchte diese Zeichenfolge zu CallDetails zuordnen – Misiu

+0

Vielen Dank für das Update, wie Sie meine ursprüngliche Option geschrieben ist nicht schlecht. Was ich wirklich will, ist in der Lage zu sein, 'ModelState.IsValid' in Methoden zu verwenden. 'DelegatingHandler' sieht wie die beste Option aus - Ich werde mein Modell sofort bekommen, wahrscheinlich wird 'ModelState.IsValid' funktionieren (bin mir da nicht sicher). Der Nachteil ist benutzerdefinierte Route. Vielleicht kann Handler im Attribut angegeben werden? Weißt du, ob das möglich ist? – Misiu

+0

@Misiu, ich weiß, dass es ** nicht ** möglich ist, einen Handler mit Attribut-Routing zu spezifizieren. Und die Validierung wird funktionieren –

Verwandte Themen