2016-06-16 9 views
8

Sto creando un pubblico REST Api utilizzando ASP.NET Core 1.0 RC2 e mi piace per registrare le richieste in entrata e le risposte in uscita.Come registrare il corpo di risposta HTTP in ASP.NET Core 1.0

Ho creato una classe middleware che viene aggiunta alla pipeline prima della chiamata ad app.UseMvc();

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 
{    
     app.UseIOMiddleware(); 
     app.UseMvc();    
} 
classe

mio Middleware si presenta così:

public class IOMiddleware 
{ 
    private readonly RequestDelegate _next; 

    public IOMiddleware(RequestDelegate next) 
    { 
     _next = next; 
    } 

    public async Task Invoke(HttpContext context) 
    { 
     LogRequest(context.Request); 

     await _next.Invoke(context);    
    } 

    private async void LogRequest(HttpRequest request) 
    { 
     using (var bodyReader = new StreamReader(request.Body)) 
     { 
      string body = await bodyReader.ReadToEndAsync(); 

      request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); 

      System.Diagnostics.Debug.Print(body); 
     } 
    } 
} 

posso leggere il flusso di richiesta di corpo e riavvolgerlo con questo esempio: Rewind request body stream, ma non sono sicuro come leggere il corpo della risposta come il flusso non è leggibile.

In Web API 2.0 avrei potuto utilizzare il metodo HttpResponseMessage.Content.ReadAsByteArrayAsync(), ma come posso ottenere la stessa cosa in ASP.Net Core 1.0 RC2?

+0

Eventuali duplicati di [Come leggere ASP.NET Nucleo Response.Body?] (Https://stackoverflow.com/questions/43403941/how-to-read-asp-net-core-response-body) –

risposta

6

Sfortunatamente se si sostituisce la richiesta con MemoryStream, lo stesso flusso verrà utilizzato per le chiamate future. Ecco l'errore: https://github.com/aspnet/KestrelHttpServer/issues/940

La soluzione è copiare flusso Request.Body alla variabile locale e impostare corpo al flusso originale alla fine.

Ti piace questa:

public async Task Invoke(HttpContext context) 
    { 
     //Workaround - copy original Stream 
     var initalBody = context.Request.Body; 

     using (var bodyReader = new StreamReader(request.Body)) 
     { 
      string body = await bodyReader.ReadToEndAsync(); 
      //Do something with body 
      //Replace write only request body with read/write memorystream so you can read from it later 

       request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); 

     //handle other middlewares 
     await _next.Invoke(context); 

     //Workaround - return back to original Stream 
     context.Request.Body = initalBody; 
    } 
+0

corretto per ora (1.0.1+) :) –

+8

La domanda richiede il corpo della risposta, non il corpo della richiesta. –

9

Il problema è che request.Body non è leggibile, solo scrivibile, in genere lo streaming verrà periodicamente scaricato dal client attraverso il cavo.

È possibile aggirare questo sostituendo il flusso e il buffering del contenuto fino al completamento del resto della pipeline.

public class IOMiddleware 
{ 
    private readonly RequestDelegate _next; 

    public IOMiddleware(RequestDelegate next) 
    { 
     _next = next; 
    } 

    public async Task Invoke(HttpContext context) 
    { 
     await LogRequest(context.Request); 

     await LogResponseAndInvokeNext(context); 
    } 

    private async Task LogRequest(HttpRequest request) 
    { 
     using (var bodyReader = new StreamReader(request.Body)) 
     { 
      string body = await bodyReader.ReadToEndAsync(); 

      request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); 
      System.Diagnostics.Debug.Print(body); 
     } 
    } 

    private async Task LogResponseAndInvokeNext(HttpContext context) 
    { 
     using (var buffer = new MemoryStream()) 
     { 
      //replace the context response with our buffer 
      var stream = context.Response.Body; 
      context.Response.Body = buffer; 

      //invoke the rest of the pipeline 
      await _next.Invoke(context); 

      //reset the buffer and read out the contents 
      buffer.Seek(0, SeekOrigin.Begin); 
      var reader = new StreamReader(buffer); 
      using (var bufferReader = new StreamReader(buffer)) 
      { 
       string body = await bufferReader.ReadToEndAsync(); 

       //reset to start of stream 
       buffer.Seek(0, SeekOrigin.Begin); 

       //copy our content to the original stream and put it back 
       await buffer.CopyToAsync(stream); 
       context.Response.Body = stream; 

       System.Diagnostics.Debug.Print($"Response: {body}"); 

      } 
     } 
    } 
} 
+0

Per leggere il corpo della richiesta, è possibile effettuare le seguenti operazioni per ottenere un flusso bufferizzato che consente di cercarlo e leggerlo più volte; 'context.Request.EnableRewind()' (metodo di estensione HttpRequest trovato in 'Microsoft.AspNetCore.Http.Internal.BufferingHelper'). – jfiskvik

+1

Non sono sicuro di quali potrebbero essere gli effetti collaterali di ciò. Poiché il buffer viene trasmesso in streaming su un socket, potrebbe produrre risultati imprevisti. Il flusso del corpo ha letto non è stato implementato per un motivo. –

+0

Avvolto in una singola classe per la registrazione di richieste e risposte, che evita anche eccezioni per i file memorizzati nella cache e dispone correttamente, su [github] (https://github.com/Hiblet/LogRequestAndResponseMiddleware). Mi piacerebbe averlo usato come ogni bug verrà scosso da esso. –

1

dopo aver cercato in tutto il mondo questa è la classe ho finito con. sta funzionando bene per me e gestisce il caso in cui c'è un'eccezione [che non ha restituito alcuna risposta, ma la registra con successo !!]. Questo è un prodotto collettivo di così tanti post online.

using Microsoft.AspNetCore.Http; 
using System; 
using System.Diagnostics; 
using System.IO; 
using System.Linq; 
using System.Threading.Tasks; 
using Microsoft.AspNet.Http.Internal; 
using Microsoft.AspNetCore.Http.Internal; 



public class LoggerMiddleware 
{ 
    private readonly RequestDelegate _next; 

    public LoggerMiddleware(RequestDelegate next) 
    { 
     _next = next; 
    } 

    public async Task Invoke(HttpContext context) 
    { 
     using (MemoryStream requestBodyStream = new MemoryStream()) 
     { 
      using (MemoryStream responseBodyStream = new MemoryStream()) 
      { 
       Stream originalRequestBody = context.Request.Body; 
       context.Request.EnableRewind(); 
       Stream originalResponseBody = context.Response.Body; 

       try 
       { 
        await context.Request.Body.CopyToAsync(requestBodyStream); 
        requestBodyStream.Seek(0, SeekOrigin.Begin); 

        string requestBodyText = new StreamReader(requestBodyStream).ReadToEnd(); 

        requestBodyStream.Seek(0, SeekOrigin.Begin); 
        context.Request.Body = requestBodyStream; 

        string responseBody = ""; 


        context.Response.Body = responseBodyStream; 

        Stopwatch watch = Stopwatch.StartNew(); 
        await _next(context); 
        watch.Stop(); 

        responseBodyStream.Seek(0, SeekOrigin.Begin); 
        responseBody = new StreamReader(responseBodyStream).ReadToEnd(); 
        AuditLogger.LogToAudit(context.Request.Host.Host, 
         context.Request.Path, context.Request.QueryString.ToString(), context.Connection.RemoteIpAddress.MapToIPv4().ToString(), 
         string.Join(",", context.Request.Headers.Select(he => he.Key + ":[" + he.Value + "]").ToList()), 
         requestBodyText, responseBody, DateTime.Now, watch.ElapsedMilliseconds); 

        responseBodyStream.Seek(0, SeekOrigin.Begin); 

        await responseBodyStream.CopyToAsync(originalResponseBody); 
       } 
       catch (Exception ex) 
       { 
        ExceptionLogger.LogToDatabse(ex); 
        byte[] data = System.Text.Encoding.UTF8.GetBytes("Unhandled Error occured. Please, try again in a while."); 
        originalResponseBody.Write(data, 0, data.Length); 
       } 
       finally 
       { 
        context.Request.Body = originalRequestBody; 
        context.Response.Body = originalResponseBody; 
       } 
      } 
     } 
    } 
}