11

I have a .NET Core Web API project with a request and response logging middleware. I registered both middleware files in the Configure method in the Startup file

app.UseMiddleware<RequestLoggingMiddleware>();
app.UseMiddleware<ResponseLoggingMiddleware>();

For now I'm just trying to log the body, the request logging seems to work fine

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate requestDelegate;

    public RequestLoggingMiddleware(RequestDelegate requestDelegate)
    {
        this.requestDelegate = requestDelegate;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        HttpRequest httpRequest = httpContext.Request;
        httpRequest.EnableBuffering();

        ReadResult bodyReadResult = await httpRequest.BodyReader.ReadAsync();
        ReadOnlySequence<byte> bodyBuffer = bodyReadResult.Buffer;

        if (bodyBuffer.Length > 0)
        {
            byte[] bodyBytes = bodyBuffer.ToArray();
            string bodyText = Encoding.UTF8.GetString(bodyBytes);

            Console.WriteLine(bodyText);
        }

        // Reset
        httpRequest.Body.Seek(0, SeekOrigin.Begin);

        await requestDelegate(httpContext);
    }
}

My response logging middleware does not have access to a BodyReader. I tried to go with this code

public class ResponseLoggingMiddleware
{
    private readonly RequestDelegate requestDelegate;

    public ResponseLoggingMiddleware(RequestDelegate requestDelegate)
    {
        this.requestDelegate = requestDelegate;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        await requestDelegate(httpContext);

        Stream responseBody = httpContext.Response.Body;

        using (StreamReader reader = new StreamReader(responseBody))
        {
            string bodyText = await reader.ReadToEndAsync();

            // Reset
            responseBody.Seek(0, SeekOrigin.Begin);

            Console.WriteLine(bodyText);
        }
    }
}

but unfortunately I get this exception

System.ArgumentException: Stream was not readable.

Does someone know how to fix it?

  • yes this is a ASP.NET Core Web API project –  Oct 19 '20 at 08:56
  • HttpResponseStream mentioned in your exception message is not readable, it's write only by design. Depending on why you are trying to do that - you might be able to use some hacks like https://stackoverflow.com/q/43403941/5311735 – Evk Oct 19 '20 at 09:14
  • Are you sure, that your code is about [`System.IO.Pipelines`](https://devblogs.microsoft.com/dotnet/system-io-pipelines-high-performance-io-in-net/) APIs? – Pavel Anikhouski Oct 19 '20 at 09:15
  • @PavelAnikhouski no I mean I'm just using some types from the namespace –  Oct 19 '20 at 09:18

3 Answers3

7

You may use StreamReader to read the request body. Below code, you may follow.

            string body = string.Empty;
            Request.EnableRewind();
            using (var reader = new StreamReader(Request.Body))
            {
                Request.Body.Seek(0, SeekOrigin.Begin);
                body = reader.ReadToEnd();
            }

In the same way, you can get a response body.

            using (var reader = new StreamReader(Response.Body))
            {
                Response.Body.Seek(0, SeekOrigin.Begin);
                body = reader.ReadToEnd();
            }

Notes: Above code is based on .net core 2.2

Below is the code supported by .net core 5

string body = string.Empty; 
using (var reader = new StreamReader(Request.Body))
{
    //Request.Body.Seek(0, SeekOrigin.Begin);
    //body = reader.ReadToEnd();
    body = await reader.ReadToEndAsync();
}

Now, you have the response in the body property, do your kinds of stuff (JSON Deserilize).

Mahi
  • 1,019
  • 9
  • 19
  • 1
    thanks for your reply. Unfortunately I don't have access to the `EnableRewind` method. But I imported the using `Microsoft.AspNetCore.Http`. But my package `Microsoft.AspNetCore.Http` does not know about the `Microsoft.AspNetCore.Http.Internal` namespace –  Oct 19 '20 at 09:24
  • ah, in core 3.0 it's `EnableBuffering` –  Oct 19 '20 at 09:27
  • Yeah, I was using .net core 2.2 – Mahi Oct 19 '20 at 09:30
  • 2
    sorry but I get this exception `System.ArgumentException: Stream was not readable.` –  Oct 19 '20 at 09:31
  • @OlafSvenson, Can you please share your code? what you have tried?. – Mahi Oct 19 '20 at 10:12
  • yes sure. I updated my question. I took this code and modified the `InvokeAsync` methods to your suggested changes: https://pastebin.com/EgZk4TYJ –  Oct 19 '20 at 11:44
1

Anyone who is looking for the solution of .net standard can use the following snippet

For ApiController

string requestBody = string.Empty;
using (var reader = new StreamReader(HttpContext.Current.Request.InputStream))
{
  reader.BaseStream.Seek(0, SeekOrigin.Begin);
  requestBody = reader.ReadToEnd();
}
Console.WriteLine(requestBody);

For regular controller

string requestBody = string.Empty;
using (var reader = new StreamReader(HttpContext.Request.InputStream))
{
   reader.BaseStream.Seek(0, SeekOrigin.Begin);
   requestBody = reader.ReadToEnd();
}
Console.WriteLine(requestBody);
Raihan Ridoy
  • 678
  • 8
  • 18
-1

Stream is an Abstract class, you must tu use a MemoryStream, check this:

        using (var ms = new MemoryStream())
        {
            var cuerpoOriginalRespuesta = contexto.Response.Body;
            contexto.Response.Body = ms;

            await siguiente(contexto);  
            ms.Seek(0, SeekOrigin.Begin);
            string respuesta = new StreamReader(ms).ReadToEnd();
            ms.Seek(0, SeekOrigin.Begin);

            await ms.CopyToAsync(cuerpoOriginalRespuesta);
            contexto.Response.Body = cuerpoOriginalRespuesta;


        }