0

In an ASP.Net Core project, I'm writing a logging middleware to log all Http responses. I was following this SO question:

public async Task InvokeAsync(HttpContext httpContext)
        {
            Stream originalResponseStream = httpContext.Response.Body;
            try
            {
                // log the response
                using (MemoryStream responseStream = new MemoryStream())
                {
                    httpContext.Response.Body = responseStream;

                    await this.nextMiddleware.Invoke(httpContext);

                    httpContext.Response.ContentLength = responseStream.Length;
                    responseStream.Seek(0, SeekOrigin.Begin);
                    await responseStream.CopyToAsync(originalResponseStream, 1024 * 16);

                    Logger.LogHttpResponse(httpContext, session);
                }
            }
            finally
            {
                httpContext.Response.Body = originalResponseStream;
            }
        }

and then I have the Logger as a seperate project and Logger.LogHttpResponse has the following code:

void LogHttpResponse(HttpContext context)
{
    var body = new StreamReader(context.Response.Body, UTF8Encoding.UTF8).ReadToEnd();
    // log body
}

The issue I'm facing right now, when creating a StreamReader, I get "Cannot access a closed Stream." exception. To get around the exception, i need to remove the using statement of MemoryStream in the middleware.

I'm not sure why the Response.Body stream was closed before leaving the using statement?

How to fix the exception properly?

Mhd
  • 2,778
  • 5
  • 22
  • 59
  • ASP.NET Core already has [HTTP logging middleware](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-logging/?view=aspnetcore-6.0) – Panagiotis Kanavos Sep 23 '21 at 15:59
  • @PanagiotisKanavos i need to make some custom changes to the logs and i believe that middleware won't help – Mhd Sep 23 '21 at 16:49
  • You can only iterate over the original response stream once. Log the memory stream instead. See [this answer](https://stackoverflow.com/questions/147941/how-can-i-read-an-http-response-stream-twice-in-c) – John Wu Sep 23 '21 at 20:19
  • Haven't you got this backwards: should be `originalResponseStream.CopyToAsync(responseStream` – Charlieface Sep 23 '21 at 20:52

1 Answers1

0

I found this in my source, I assume I had the same problem with the exception. The requests body could only be read once, so enable buffering of the stream an reset the position of the stream to 0 after reading

public string GetBody(HttpContext context)
{
    context.Request.EnableBuffering();
    var body = await new StreamReader(msg.HttpContext.Request.Body).ReadToEndAsync();
    msg.HttpContext.Request.Body.Position = 0;
    return body;
}
DotNetDev
  • 205
  • 1
  • 10