Skip to content

Instantly share code, notes, and snippets.

@elanderson
Created February 7, 2017 12:46
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save elanderson/c50b2107de8ee2ed856353dfed9168a2 to your computer and use it in GitHub Desktop.
Save elanderson/c50b2107de8ee2ed856353dfed9168a2 to your computer and use it in GitHub Desktop.
Request Response Logging Middleware for ASP.NET Core
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public RequestResponseLoggingMiddleware(RequestDelegate next,
ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory
.CreateLogger<RequestResponseLoggingMiddleware>();
}
public async Task Invoke(HttpContext context)
{
_logger.LogInformation(await FormatRequest(context.Request));
var originalBodyStream = context.Response.Body;
using (var responseBody = new MemoryStream())
{
context.Response.Body = responseBody;
await _next(context);
_logger.LogInformation(await FormatResponse(context.Response));
await responseBody.CopyToAsync(originalBodyStream);
}
}
private async Task<string> FormatRequest(HttpRequest request)
{
var body = request.Body;
request.EnableRewind();
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
await request.Body.ReadAsync(buffer, 0, buffer.Length);
var bodyAsText = Encoding.UTF8.GetString(buffer);
request.Body = body;
return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}
private async Task<string> FormatResponse(HttpResponse response)
{
response.Body.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(response.Body).ReadToEndAsync();
response.Body.Seek(0, SeekOrigin.Begin);
return $"Response {text}";
}
}
public static class RequestResponseLoggingMiddlewareExtensions
{
public static IApplicationBuilder UseRequestResponseLogging(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestResponseLoggingMiddleware>();
}
}
@JustinJohnWilliams
Copy link

This seems to be working for all of my GETs, however, my POSTs do not seem to send the body to the action method (it always shows up null). It logs correctly with the body, so I know I sent it with the request, the controller action seems to just get passed null.

[HttpPost("some_route")] public IActionResult SomeRoute(Guid id, [FromBody, Required] SomeRequest request) { //id is populated // request is null

removing the middleware, the request comes through just fine. any ideas?

@JustinJohnWilliams
Copy link

Added a functioning example of what i'm seeing here: https://github.com/JustinJohnWilliams/RequestLogging

Also I notice when launching with the debugger the initial request for GET /values comes in twice. The second time around the response isn't logged.

@richardscholten73
Copy link

Getting the same problem as JustinJohnWilliams. Doing a post seems to be corrupting te request body stream and results in a null object in the Controller Action.

i Tried setting the stream position back to 0, but no success.

Any one?

@AdamLenda
Copy link

@ajithesh
Copy link

ajithesh commented Dec 6, 2017

I am able to get the response when i do a GET. But when i do a POST, i expect my data to come to Request object. But that is missing. Can someone put your solutions?

@petret
Copy link

petret commented Jan 12, 2018

@ajithesh try the following:

  1. Swap lines 33 and 34 - so the .EnableRewind()is before the assignment;
  2. Between line 38 and 39 insert body.Seek(0, SeekOrigin.Begin).

@israelquiroz93
Copy link

@petret worked like a charm

@LorenzoSerino
Copy link

Great solution! But the statuscode 204 it's very danger for VS2017!!!

I add the following "if" before 27

//Because you change the response body which is not allowed on a 204.
if (context.Response.StatusCode!=204) 
     await responseBody.CopyToAsync(originalBodyStream);

@mikeloguay
Copy link

Thanks @petret, the same behaviour (request body null on POSTs) on my side, and thid change solved it.

@elanderson it would be nice if you update the code with the fix.

Many thanks.

@georgekargakis
Copy link

georgekargakis commented Sep 14, 2018

@elanderson thank you for sharing the approach of using a middleware in order to log request/responses in an asp.netcore application. However the specific solution might cause memory leak in case of large body in request and response. Please check an alternative approach that might eradicate the risk of memory leak. Feel free to send your feedback so we can end up in a more elegant solution.

@fernando-facile
Copy link

fernando-facile commented Sep 27, 2018

Hello all.
With every solutions posted here, I my Webapi core 2.1 still not working with POSTS requests, send for example by PostMan.
The POST requests simple does not trigger Invoke method. The GET request works like a charm.
Even doing mr. @petret alterations.
Any option?
Thanks.

@CoviloMilos
Copy link

Remove this part from code and it will forward body of post.
var body = request.Body;
request.EnableRewind();

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

@c0d3sling3r
Copy link

c0d3sling3r commented Apr 6, 2019

@petret It worked for me too, Thanks.
I had issue in POST requests which request was sending null.

@alesshosry
Copy link

@shojajou check this link, you have to reset the position of the stream

@Bazooka2091
Copy link

Bazooka2091 commented Nov 27, 2019

I'm crying! Thank you all !!!

@ajithesh try the following:

  1. Swap lines 33 and 34 - so the .EnableRewind()is before the assignment;
  2. Between line 38 and 39 insert body.Seek(0, SeekOrigin.Begin).

Thanks!

@ghasemi1994
Copy link

@petret
thank uuuuuuuuuuuuu woooooooooooow!!!!!!!!

@unseensenpai
Copy link

unseensenpai commented Sep 21, 2023

I struggelered a lot with body request null.

Tried on .NET 8 and working fine. Added some cool details and json limit (100 char) for log file size.

Request Response Logging in .Net 8
You can find all files here for implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment