2012-08-24 17 views
12

Quando si tenta di eseguire un oggetto in un'azione in un controller, sembra sporadicamente nullo. Ho scoperto che è dovuto allo ReadAsStringAsync() nell'override SendAsync() dello DelegatingHandler. Il problema è con il contenuto. Quando il mio cliente invia un corpo del contenuto e viene letto nel logger, non viene mai letto dal Controller Action Invoker (o potrebbe trovarsi da qualche parte nello JsonFormatter). Sospetto che la successiva chiamata a Content.ReadAsStringAsync() non generi un'eccezione ma non restituisca il corpo del contenuto previsto (vengono restituite alcune informazioni che indicano che la lettura asincrona è stata completata).HttpRequestMessage.Content viene perso quando viene letto in una registrazione DelegatingHandler in API Web ASP.Net

Ma il mio problema rimane in quanto voglio leggere un parametro [FromBody] in un'azione ed è null quando il race condition di Content.ReadStringAsync è vinta dalla DelegatingHandler. Quando lo vince lo JsonFormatter, ottengo l'oggetto ma è raro (solo all'avvio del servizio).

Ecco il mio codice DelegatingHandler:

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

Qualcuno ha un indizio per la soluzione di questo problema?

+0

Dimenticato di parlare di questo è RC. Sospetto che l'osservazione rimanga la stessa anche in RTM – Sando

risposta

9

Questo è di progettazione. Nell'API Web ASP.NET il contenuto del corpo viene trattato come un flusso forward-only che può essere letto solo una volta.

Si potrebbe provare a utilizzare ASP.NET Web API Tracing ma non ho prova con richiesta POST eppure così io non sono sicuro di come/se sta tracciando il corpo della richiesta (si sta tracciando parametri per richiesta GET di sicuro). Si può leggere di più qui:

+2

Grazie.Penso che per le mie esigenze posso vivere senza scrivere il contenuto da registrare. Tuttavia questo dovrebbe essere un 'Punto da notare' per tutti gli sviluppatori di web api che cercano di registrare i contenuti e anche usando [FromBody] in Azioni POST – Sando

2

ma se si utilizza il codice qui sotto nella SendAsync funziona correttamente

 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

Ma perché funziona quando si fa questo? –

+0

Non è la stessa cosa. Stai leggendo il contenuto della richiesta e non il contenuto della risposta, che peraltro è anche il motivo per cui funziona. – Adam

5

Il metodo ReadAsStreamAsync restituisce il contenuto del corpo.

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

Attenzione che il blocco using chiuderà il vapore sottostante. – ivanmartinvalle

1

questo ha funzionato per me:

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

restituito per me la rappresentazione JSON del mio oggetto parametro, così ho potuto usarlo per la gestione delle eccezioni e la registrazione.

Trovato come risposta here

+0

Ma qui non possiamo ricevere le intestazioni di richiesta –

1

Ecco accettato è quello che ho finito per fare:

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

@ risposta di pirimoglu di utilizzare un blocco "con" non ha funzionato per me, dal momento che quando il lettore è stato ceduto anche il flusso sottostante è stato chiuso.

+0

non funziona :( –

Problemi correlati