DEV Community

Kabue Charles
Kabue Charles

Posted on

3 1

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);
view raw Startup.cs hosted with ❤ by GitHub

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay