2015-07-13 12 views
6

Sto provando a leggere il corpo in un middleware per scopi di autenticazione, ma quando la richiesta arriva al controller api l'oggetto è vuoto mentre il corpo è già stato letto. C'è un modo per aggirare questo. Sto leggendo il corpo come questo nel mio middleware.Leggi corpo della richiesta due volte

var buffer = new byte[ Convert.ToInt32(context.Request.ContentLength) ]; 
await context.Request.Body.ReadAsync(buffer, 0, buffer.Length); 
var body = Encoding.UTF8.GetString(buffer); 
+1

Solo per curiosità: perché stai leggendo il corpo della richiesta quando si esegue l'autenticazione? –

+0

È un'API in cui è richiesta anche la richiesta ad esempio ApiKey quando si genera la risposta e anche un hash per proteggere la richiesta. Che non ha senso essere nelle intestazioni. –

+0

Un [esempio] (http://stackoverflow.com/questions/42561350/read-json-post-data-in-asp-net-core-mvc/42922579#42922579) di utilizzo di EnableRewind di PinPoint per leggere json dal corpo. –

risposta

14

Se stai usando application/x-www-form-urlencoded o multipart/form-data, si può tranquillamente chiamare context.Request.ReadFormAsync() più volte in quanto restituisce un'istanza memorizzata nella cache per le chiamate successive.

Se si utilizza un tipo di contenuto diverso, è necessario memorizzare manualmente la richiesta e sostituire il corpo della richiesta con uno stream riavvolgibile come MemoryStream. Ecco come si potrebbe fare con un middleware in linea (è necessario registrarsi al più presto nella vostra pipeline):

app.Use(next => async context => { 
    // Keep the original stream in a separate 
    // variable to restore it later if necessary. 
    var stream = context.Request.Body; 

    // Optimization: don't buffer the request if 
    // there was no stream or if it is rewindable. 
    if (stream == Stream.Null || stream.CanSeek) { 
     await next(context); 

     return; 
    } 

    try { 
     using (var buffer = new MemoryStream()) { 
      // Copy the request stream to the memory stream. 
      await stream.CopyToAsync(buffer); 

      // Rewind the memory stream. 
      buffer.Position = 0L; 

      // Replace the request stream by the memory stream. 
      context.Request.Body = buffer; 

      // Invoke the rest of the pipeline. 
      await next(context); 
     } 
    } 

    finally { 
     // Restore the original stream. 
     context.Request.Body = stream; 
    } 
}); 

È anche possibile utilizzare la BufferingHelper.EnableRewind() estensione, che è parte del pacchetto Microsoft.AspNet.Http: si basa su un approccio simile ma si basa su un flusso speciale che inizia buffer di dati in memoria e bobine di tutto per un file temporaneo sul disco quando viene raggiunta la soglia:

app.Use(next => context => { 
    context.Request.EnableRewind(); 

    return next(context); 
}); 

FYI: un middleware buffer sarà probabilmente aggiunto alla vNext in futuro.

+2

Sono curioso di vedere il caching. Questa parte del contratto o i dettagli di implementazione potrebbero cambiare in futuro? –

+1

@Pinpoint Grazie non ha pensato che fosse una buona idea. –

+0

@SimonBelanger nel prossimo futuro, gli host e il middleware probabilmente condivideranno un contratto comune per indicare se supportano il buffering o meno. Puoi trovare maggiori dettagli su questa pagina wiki per maggiori informazioni: https://github.com/aspnet/HttpAbstractions/wiki/Rolling-Notes – Pinpoint

3

di utilizzo per menzione di PinPoint di EnableRewind

Startup.cs 
using Microsoft.AspNetCore.Http.Internal; 

Startup.Configure(...){ 
... 
//Its important the rewind us added before UseMvc 
app.Use(next => context => { context.Request.EnableRewind(); return next(context); }); 
app.UseMvc() 
... 
} 

Poi, nel tuo middleware basta riavvolgere e rileggere

private async Task GenerateToken(HttpContext context) 
    { 
    context.Request.EnableRewind(); 
    string jsonData = new StreamReader(context.Request.Body).ReadToEnd(); 
    ... 
    }