Skip to content

Instantly share code, notes, and snippets.

@mckabue
Last active January 26, 2024 08:20
Show Gist options
  • Save mckabue/f661b15fe0c59ee29b2d4541d38de50f to your computer and use it in GitHub Desktop.
Save mckabue/f661b15fe0c59ee29b2d4541d38de50f to your computer and use it in GitHub Desktop.
An ASP.NET CORE Request - Response Logger Middleware
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.IO;
// https://exceptionnotfound.net/using-middleware-to-log-requests-and-responses-in-asp-net-core/
// https://gist.github.com/elanderson/c50b2107de8ee2ed856353dfed9168a2
// https://stackoverflow.com/a/52328142/3563013
// https://stackoverflow.com/a/43404745/3563013
// https://gist.github.com/elanderson/c50b2107de8ee2ed856353dfed9168a2#gistcomment-2319007
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
private readonly Action<RequestProfilerModel> _requestResponseHandler;
private const int ReadChunkBufferLength = 4096;
public RequestResponseLoggingMiddleware(RequestDelegate next, Action<RequestProfilerModel> requestResponseHandler)
{
_next = next;
_recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
_requestResponseHandler = requestResponseHandler;
}
public async Task Invoke(HttpContext context)
{
var model = new RequestProfilerModel
{
RequestTime = new DateTimeOffset(),
Context = context,
Request = await FormatRequest(context)
};
Stream originalBody = context.Response.Body;
using (MemoryStream newResponseBody = _recyclableMemoryStreamManager.GetStream())
{
context.Response.Body = newResponseBody;
await _next(context);
newResponseBody.Seek(0, SeekOrigin.Begin);
await newResponseBody.CopyToAsync(originalBody);
newResponseBody.Seek(0, SeekOrigin.Begin);
model.Response = FormatResponse(context, newResponseBody);
model.ResponseTime = new DateTimeOffset();
_requestResponseHandler(model);
}
}
private string FormatResponse(HttpContext context, MemoryStream newResponseBody)
{
HttpRequest request = context.Request;
HttpResponse response = context.Response;
return $"Http Response Information: {Environment.NewLine}" +
$"Schema:{request.Scheme} {Environment.NewLine}" +
$"Host: {request.Host} {Environment.NewLine}" +
$"Path: {request.Path} {Environment.NewLine}" +
$"QueryString: {request.QueryString} {Environment.NewLine}" +
$"StatusCode: {response.StatusCode} {Environment.NewLine}" +
$"Response Body: {ReadStreamInChunks(newResponseBody)}";
}
private async Task<string> FormatRequest(HttpContext context)
{
HttpRequest request = context.Request;
return $"Http Request Information: {Environment.NewLine}" +
$"Schema:{request.Scheme} {Environment.NewLine}" +
$"Host: {request.Host} {Environment.NewLine}" +
$"Path: {request.Path} {Environment.NewLine}" +
$"QueryString: {request.QueryString} {Environment.NewLine}" +
$"Request Body: {await GetRequestBody(request)}";
}
public async Task<string> GetRequestBody(HttpRequest request)
{
request.EnableBuffering();
request.EnableRewind();
using (var requestStream = _recyclableMemoryStreamManager.GetStream())
{
await request.Body.CopyToAsync(requestStream);
request.Body.Seek(0, SeekOrigin.Begin);
return ReadStreamInChunks(requestStream);
}
}
private static string ReadStreamInChunks(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
string result;
using (var textWriter = new StringWriter())
using (var reader = new StreamReader(stream))
{
var readChunk = new char[ReadChunkBufferLength];
int readChunkLength;
//do while: is useful for the last iteration in case readChunkLength < chunkLength
do
{
readChunkLength = reader.ReadBlock(readChunk, 0, ReadChunkBufferLength);
textWriter.Write(readChunk, 0, readChunkLength);
} while (readChunkLength > 0);
result = textWriter.ToString();
}
return result;
}
public class RequestProfilerModel
{
public DateTimeOffset RequestTime { get; set; }
public HttpContext Context { get; set; }
public string Request { get; set; }
public string Response { get; set; }
public DateTimeOffset ResponseTime { get; set; }
}
}
// USAGE in `Startup.cs`
Action<RequestProfilerModel> requestResponseHandler = requestProfilerModel =>
{
Debug.Print(requestProfilerModel.Request);
Debug.Print(Environment.NewLine);
Debug.Print(requestProfilerModel.Response);
};
app.UseMiddleware<RequestResponseLoggingMiddleware>(requestResponseHandler);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment