How to log the HTTP Response Body in ASP.NET Core 1.0
Unfortunately if you replace Request with MemoryStream, the same stream will be used for future calls. Here is the bug: https://github.com/aspnet/KestrelHttpServer/issues/940
The workaround is to copy Request.Body stream to local variable and set Body back to original stream in the end.
Like this:
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;
}
The problem is that request.Body
is not readable, only writable - typically the stream will periodically flushed to the client across the wire.
You can get round this by replacing the stream and buffering the content until the rest of the pipeline has completed.
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}");
}
}
}
}