Lesen Sie HttpContent im WebApi-Controller

74

Wie kann ich den Inhalt der PUT-Anforderung in der MVC webApi-Controller-Aktion lesen?

[HttpPut]
public HttpResponseMessage Put(int accountId, Contact contact)
{
    var httpContent = Request.Content;
    var asyncContent = httpContent.ReadAsStringAsync().Result;
...

Ich bekomme hier eine leere Zeichenfolge :(

Was ich tun muss, ist: herauszufinden, "welche Eigenschaften" in der ersten Anforderung geändert / gesendet wurden (dh wenn das ContactObjekt 10 Eigenschaften hat und ich nur 2 davon aktualisieren möchte, sende ich und Objekte mit nur zwei Eigenschaften, etwas wie das:

{

    "FirstName": null,
    "LastName": null,
    "id": 21
}

Das erwartete Endergebnis ist

List<string> modified_properties = {"FirstName", "LastName"}
Marty
quelle

Antworten:

138

Der Hauptteilinhalt in der ASP.NET-Web-API wird standardmäßig als Nur-Weiterleitungs-Stream behandelt, der nur einmal gelesen werden kann.

Der erste Lesevorgang in Ihrem Fall erfolgt, wenn die Web-API Ihr Modell bindet. Danach Request.Contentwird nichts mehr zurückgegeben.

Sie können die contactaus Ihren Aktionsparametern entfernen , den Inhalt abrufen und manuell in ein Objekt deserialisieren (z. B. mit Json.NET):

[HttpPut]
public HttpResponseMessage Put(int accountId)
{
    HttpContent requestContent = Request.Content;
    string jsonContent = requestContent.ReadAsStringAsync().Result;
    CONTACT contact = JsonConvert.DeserializeObject<CONTACT>(jsonContent);
    ...
}

Dies sollte den Trick tun (vorausgesetzt, dies accountIdist ein URL-Parameter, damit er nicht als gelesener Inhalt behandelt wird).

tpeczek
quelle
Danke. Und ja, die Konto-ID ist ein URL-Parameter.
Marty
1
Ich frage mich, ob Sie einen MessageHandler mit dem Namen LoadIntoBuffer () für den Anforderungsinhalt erstellen könnten, bevor der Modellordner aktiviert wurde.
Darrel Miller
@DarrelMiller Ich bin mir nicht sicher, ob die ASP.NET-Web-API das Modell noch binden würde - muss getestet werden.
Tpeczek
Wird dies zu Deadlocking führen?
Airn5475
3
@ Airn5475 Wie es ist, kann es sein. Sie sollten die Aktionssignatur auf asynchron umstellen und "Warten" verwenden.
Tpeczek
19

Sie können Ihren CONTACT-Parameter folgendermaßen beibehalten:

using (var stream = new MemoryStream())
{
    var context = (HttpContextBase)Request.Properties["MS_HttpContext"];
    context.Request.InputStream.Seek(0, SeekOrigin.Begin);
    context.Request.InputStream.CopyTo(stream);
    string requestBody = Encoding.UTF8.GetString(stream.ToArray());
}

Hat mir die JSON-Darstellung meines Parameterobjekts zurückgegeben, damit ich sie für die Ausnahmebehandlung und Protokollierung verwenden kann.

Hier als akzeptierte Antwort gefunden

Anytoe
quelle
4

Auch wenn diese Lösung offensichtlich erscheint, wollte ich sie nur hier posten, damit der nächste sie schneller googelt.

Wenn Sie das Modell weiterhin als Parameter in der Methode haben möchten, können Sie ein erstellen, DelegatingHandlerum den Inhalt zu puffern.

internal sealed class BufferizingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        await request.Content.LoadIntoBufferAsync();
        var result = await base.SendAsync(request, cancellationToken);
        return result;
    }
}

Und fügen Sie es den globalen Nachrichtenhandlern hinzu:

configuration.MessageHandlers.Add(new BufferizingHandler());

Diese Lösung basiert auf der Antwort von Darrel Miller .

Auf diese Weise werden alle Anforderungen gepuffert.

derwasp
quelle
Ich denke, das könnte meinen Server perfekt machen ... aber es könnte sich als nützlich erweisen.
Hunter-Orionnoir