Skip to content

Instantly share code, notes, and snippets.

@afruzan
Created January 28, 2021 12:42
Show Gist options
  • Save afruzan/fb380ba6235b38682bf4bbfe63018810 to your computer and use it in GitHub Desktop.
Save afruzan/fb380ba6235b38682bf4bbfe63018810 to your computer and use it in GitHub Desktop.
logging full request and responce (including body) in asp.net core (tested on 5 but should supports 3.1).
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace TVS.Web.DataReceiverService.Middlewares
{
/*
based on https://github.com/matthew-daddario/AspNetCoreRequestResponseLogger with some improvments.
# How To:
add midleware at startup.cs Configure method:
```c#
app.UseMiddleware<Middlewares.RequestResponseLoggingMiddleware>();
```
sample configs at appsettings.json:
```json
"Logging": {
"RequestResponseLogging": {
"Enabled": true,
"LogBodyEnabled": true
}
}
```
*/
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly bool _isEnabled;
private readonly bool _logBodyIsEnabled;
private readonly ILogger<RequestResponseLoggingMiddleware> _logger;
public RequestResponseLoggingMiddleware(RequestDelegate next, IConfiguration config, ILogger<RequestResponseLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
_isEnabled = config.GetValue<bool>("RequestResponseLogging.Enabled", true);
_logBodyIsEnabled = config.GetValue<bool>("RequestResponseLogging.LogBodyEnabled", true);
}
public async Task InvokeAsync(HttpContext httpContext)
{
// Middleware is enabled only when the EnableRequestResponseLogging config value is set.
if (_isEnabled)
{
_logger.LogInformation($"HTTP Request Information:\n" +
$"\tMethod: {httpContext.Request.Method}\n" +
$"\tPath: {httpContext.Request.Path}\n" +
$"\tQueryString: {httpContext.Request.QueryString}\n" +
$"\tHeaders: {FormatHeaders(httpContext.Request.Headers)}\n" +
$"\tSchema: {httpContext.Request.Scheme}\n" +
$"\tHost: {httpContext.Request.Host}\n" +
(_logBodyIsEnabled ? $"\tBody: {await ReadBodyFromRequest(httpContext.Request)}" : ""));
// Temporarily replace the HttpResponseStream, which is a write-only stream, with a MemoryStream to capture it's value in-flight.
var originalResponseBody = httpContext.Response.Body;
using var newResponseBody = new MemoryStream();
httpContext.Response.Body = newResponseBody;
// Call the next middleware in the pipeline
await _next(httpContext);
newResponseBody.Seek(0, SeekOrigin.Begin);
var responseBodyText = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
_logger.LogInformation($"HTTP Response Information:\n" +
$"\tStatusCode: {httpContext.Response.StatusCode}\n" +
$"\tContentType: {httpContext.Response.ContentType}\n" +
$"\tHeaders: {FormatHeaders(httpContext.Response.Headers)}\n" +
(_logBodyIsEnabled ? $"\tBody: {responseBodyText}" : ""));
newResponseBody.Seek(0, SeekOrigin.Begin);
await newResponseBody.CopyToAsync(originalResponseBody);
}
else
{
await _next(httpContext);
}
}
private static string FormatHeaders(IHeaderDictionary headers) => string.Join(", ", headers.Select(kvp => $"{{{kvp.Key}: {string.Join(", ", kvp.Value)}}}"));
private static async Task<string> ReadBodyFromRequest(HttpRequest request)
{
// Ensure the request's body can be read multiple times (for the next middlewares in the pipeline).
request.EnableBuffering();
using var streamReader = new StreamReader(request.Body, leaveOpen: true);
var requestBody = await streamReader.ReadToEndAsync();
// Reset the request's body stream position for next middleware in the pipeline.
request.Body.Position = 0;
return requestBody;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment