2012-08-24 6 views
12

Beim Versuch, ein Objekt in einer Aktion in einem Controller sporadisch scheint es Null zu sein. Ich entdeckte, dass es aufgrund der ReadAsStringAsync() in der SendAsync() Override der DelegatingHandler ist. Das Problem ist mit dem Inhalt. Wenn mein Client einen Inhaltskörper sendet und dieser im Logger gelesen wird, wird er niemals vom Controller Action Invoker gelesen (oder er befindet sich irgendwo in der JsonFormatter). Ich vermute, dass der nachfolgende Aufruf an Content.ReadAsStringAsync() eine Ausnahme nicht auslöst, aber auch nicht den erwarteten Inhaltskörper zurückgibt (einige Informationen werden zurückgegeben, die besagen, dass der Async-Lesevorgang abgeschlossen ist).HttpRequestMessage.Content ist verloren, wenn es in einer Protokollierung gelesen wird DelegatingHandler in ASP.NET-Web-API

Aber mein Problem bleibt, da ich einen [FromBody] Parameter in einer Aktion lesen möchte und es null ist, wenn die RaceCondition Content.ReadStringAsync durch die gewonnen wird. Wenn JsonFormatter es zwar gewinnt, bekomme ich das Objekt aber das ist selten (nur beim Dienststart).

Hier ist mein DelegatingHandler Code:

public class LogHandler : DelegatingHandler 
{ 

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     var apiRequest = new WebApiUsageRequest(request); 
     WriteLog(apiRequest); 
     request.Content.ReadAsStringAsync().ContinueWith(t => 
     { 
      apiRequest.Content = t.Result; 
      WriteLog(apiRequest); 
     }); 

     return base.SendAsync(request, cancellationToken).ContinueWith(task => 
     { 
      var apiResponse = new WebApiUsageResponse(task.Result); 
      apiResponse.Content = task.Result.Content != null ? task.Result.Content.ReadAsStringAsync().Result : null; 
      WriteLog(apiResponse); 
      return task.Result; 
     }); 
    } 
} 

Hat jemand einen Hinweis auf die Lösung dieses Problems haben?

+0

Vergessen zu erwähnen, das ist RC. Ich vermute, die Beobachtung bleibt in RTM auch gleich – Sando

Antwort

9

Dies ist von Design. In der ASP.NET-Web-API wird der Body-Inhalt als Nur-Vorwärts-Stream behandelt, der nur einmal gelesen werden kann.

Sie könnten versuchen, ASP.NET-Web-API Tracing, aber ich habe es noch nicht mit POST-Anfrage zu testen, ich bin mir nicht sicher, wie/wenn es den Anfragekörper verfolgt (es verfolgt Parameter für GET-Anfrage sicher). Sie können mehr hier lesen:

+2

Danke.Ich denke für meine Anforderungen kann ich damit leben, den Inhalt nicht zu schreiben. Dies sollte jedoch ein "Point to Note" für alle Web-API-Entwickler sein, die versuchen, Inhalte zu protokollieren und auch [FromBody] in POST-Aktionen zu verwenden. – Sando

2

aber wenn Sie den Code in der unten stehenden SendAsync verwenden funktioniert es richtig

 if (request.Content != null) 
     { 
      request.Content.ReadAsByteArrayAsync().ContinueWith 
       (
        (task) => 
        { 

          var xxx = System.Text.UTF8Encoding.UTF8.GetString(task.Result); 
        }); 
     } 
     return base.SendAsync(request, cancellationToken) //than call the base 

. . .

+0

Aber warum funktioniert es dabei? –

+0

Das ist nicht das Gleiche. Sie lesen den Inhalt der Anfrage nicht den Inhalt der Antwort, was übrigens auch der Grund dafür ist, dass sie funktioniert – Adam

5

Die ReadAsStreamAsync-Methode gibt den Body-Inhalt zurück.

var body = string.Empty; 
using (var reader = new StreamReader(request.Content.ReadAsStreamAsync().Result)) 
{ 
    reader.BaseStream.Seek(0, SeekOrigin.Begin); 
    body = reader.ReadToEnd(); 
} 
+0

Beachten Sie, dass der verwendete Block den zugrunde liegenden Dampf schließt. – ivanmartinvalle

1

Dieser arbeitete für mich:

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

für mich kehrten die json Darstellung meines Parameter-Objekt, so konnte ich es für die Ausnahmebehandlung und Protokollierung verwenden.

Gefunden als akzeptierte Antwort here

+0

Aber hier können wir keine Anfrage Header –

1

Hier ist, was ich tun endete:

public string SafelyReadContent(HttpRequestMessage request) 
{ 
    var stream = request.Content.ReadAsStreamAsync().Result; 
    var reader = new StreamReader(stream); 
    var result = reader.ReadToEnd(); 
    stream.Seek(0, SeekOrigin.Begin); 

    return result; 
} 

@ Antwort des pirimoglu einen „mit“ Block der Verwendung nicht für mich arbeiten, da, wenn der Leser angeordnet wurde , der zugrunde liegende Strom wurde ebenfalls geschlossen.

+0

es funktioniert nicht erhalten :( –

Verwandte Themen