Introduction
In modern web applications, monitoring API requests and responses is essential for debugging, auditing, and performance optimization. ASP.NET Core provides a powerful middleware pipeline, making it easy to create custom middleware that can intercept HTTP traffic.
In this article, we’ll walk through building a custom middleware for request and response logging — from basic setup to production-ready enhancements like exception handling and structured logging.
What is Middleware?
Middleware in ASP.NET Core is software that sits in the request-response pipeline. Each middleware component:
Processes incoming requests,
Optionally modifies or logs them,
And can pass them along to the next middleware in the pipeline.
By placing custom logic here, you can centrally handle tasks like logging, authentication, error handling, or response modification.
Step 1: Setting Up the Middleware Class
Let’s create a middleware called RequestResponseLoggingMiddleware.
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Text;
using System.Threading.Tasks;
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestResponseLoggingMiddleware> _logger;
public RequestResponseLoggingMiddleware(RequestDelegate next, ILogger<RequestResponseLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// Log Request
context.Request.EnableBuffering();
var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
context.Request.Body.Position = 0;
_logger.LogInformation("Incoming Request: {method} {url} \nBody: {body}",
context.Request.Method, context.Request.Path, requestBody);
// Capture Response
var originalBodyStream = context.Response.Body;
using var responseBody = new MemoryStream();
context.Response.Body = responseBody;
await _next(context); // Call next middleware
// Log Response
context.Response.Body.Seek(0, SeekOrigin.Begin);
var responseText = await new StreamReader(context.Response.Body).ReadToEndAsync();
context.Response.Body.Seek(0, SeekOrigin.Begin);
_logger.LogInformation("Response: {statusCode}\nBody: {body}",
context.Response.StatusCode, responseText);
await responseBody.CopyToAsync(originalBodyStream);
}
}

Join the conversation! Your thoughts help the community grow.