2016-06-16 7 views
8

Я создаю публичный REST Api с использованием ASP.NET Core 1.0 RC2 и хотел бы регистрировать входящие запросы и исходящие ответы.Как зарегистрировать тело ответа HTTP в ASP.NET Core 1.0

Я создал класс промежуточного программного обеспечения, который добавляется в конвейер перед вызовом app.UseMvc();

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

Мой Middleware класс выглядит следующим образом:

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

Я могу прочитать поток запроса тела и перематывать его, используя этот пример: Rewind request body stream, но я не знаю, как читать тело ответа, как поток не читается.

В Web API 2.0 я мог бы использовать метод HttpResponseMessage.Content.ReadAsByteArrayAsync(), но как я могу выполнить то же самое в ASP.NET Core 1.0 RC2?

+0

Возможный дубликат [Как читать ASP.NET Core, Response.Body?] (Https://stackoverflow.com/questions/43403941/how-to-read-asp-net-core-response-body) –

ответ

6

К сожалению, если вы замените Request с помощью MemoryStream, тот же поток будет использоваться для будущих вызовов. Вот ошибка: https://github.com/aspnet/KestrelHttpServer/issues/940

Чтобы обойти эту проблему, чтобы скопировать Request.Body поток в локальную переменную и установите тело обратно в исходный поток в конце концов.

Как это:

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

исправлено на данный момент (1.0.1+) :) –

+8

Вопрос запрашивает тело ответа, а не запрашивает тело. –

9

Проблема заключается в том, что request.Body не читается, доступен только для записи - обычно поток периодически промывается клиентом по проводу.

Вы можете обойти это, заменив поток и буферизируя контент до завершения остальной части конвейера.

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

Для чтения тела запроса вы можете сделать следующее, чтобы получить буферный поток, который позволяет вам искать и читать его несколько раз; 'context.Request.EnableRewind()' (метод расширения HttpRequest, найденный в 'Microsoft.AspNetCore.Http.Internal.BufferingHelper'). – jfiskvik

+1

Я не уверен, какие побочные эффекты от этого могут быть. Поскольку буфер передается через сокет, это может привести к неожиданным результатам. Поток тела не читается без объяснения причин. –

+0

Включил это в один класс для ведения журнала запросов и ответов, который также избегает исключений для кешированных файлов и правильно размещается на [github] (https://github.com/Hiblet/LogRequestAndResponseMiddleware). Я бы хотел, чтобы это использовалось, поскольку любые ошибки будут потрясены из него. –

1

после поиска везде это класс я закончил с. он работает отлично для меня и обрабатывает случай, когда есть исключение [он использовал, чтобы не возвращать никакого ответа, но записывает его успешно!]. Это коллективная продукция стольких сообщений в Интернете.

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; 
       } 
      } 
     } 
    } 
} 
Смежные вопросы