๐ง  Introduction
ASP.NET Core is known for its modular pipeline, where middleware components play a critical role in handling HTTP requests and responses. Middleware can perform a variety of tasks such as request logging, authentication, routing, response modification, error handling, and more.
While ASP.NET Core offers built-in middleware like UseRouting, UseAuthorization, etc., there are many scenarios where you’ll want to create your own.
In this article, we’ll explore:
๐ง What is Middleware?
Middleware in ASP.NET Core is software that's assembled into the HTTP request pipeline. Each piece of middleware:
	- 
	Processes an incoming request 
- 
	Can optionally pass the request to the next component 
- 
	Optionally processes the response on the way back 
Conceptually, it looks like a chain:
Request --> Middleware 1 --> Middleware 2 --> Middleware 3 --> ... --> Response
๐ Why Create Custom Middleware?
You should consider writing custom middleware when:
	- 
	You want reusable, centralized behavior (like logging or metrics) 
- 
	You need to intercept or transform requests or responses 
- 
	Built-in middleware doesn’t meet your specific need 
๐ฆ Project Setup
Let’s build a simple ASP.NET Core Web API project to demonstrate.
dotnet new webapi -n CustomMiddlewareDemo
cd CustomMiddlewareDemo
๐จ Example 1: Logging Request Duration
โจ Purpose:
Log how long each HTTP request takes.
๐ Step 1. Create the Middleware
File: Middleware/RequestTimingMiddleware.cs
using Microsoft.AspNetCore.Http;
using System.Diagnostics;
using System.Threading.Tasks;
using System;
namespace CustomMiddlewareDemo.Middleware
{
    public class RequestTimingMiddleware
    {
        private readonly RequestDelegate _next;
        public RequestTimingMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            var stopwatch = Stopwatch.StartNew();
            await _next(context); // Call next middleware
            stopwatch.Stop();
            var elapsedMs = stopwatch.ElapsedMilliseconds;
            Console.WriteLine($"โฑ๏ธ [{DateTime.Now}] Request {context.Request.Method} {context.Request.Path} took {elapsedMs}ms");
        }
    }
}
๐ Step 2. Create an Extension Method
File: Middleware/RequestTimingMiddlewareExtensions.cs
using Microsoft.AspNetCore.Builder;
namespace CustomMiddlewareDemo.Middleware
{
    public static class RequestTimingMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestTiming(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestTimingMiddleware>();
        }
    }
}
๐ Step 3. Register Middleware in Program.cs
using CustomMiddlewareDemo.Middleware;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseRequestTiming(); // Our custom middleware
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
๐งช Step 4. Add a Test Endpoint
File: Controllers/TestController.cs
using Microsoft.AspNetCore.Mvc;
using System.Threading;
namespace CustomMiddlewareDemo.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class TestController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            Thread.Sleep(200); // Simulate processing delay
            return Ok("Middleware test successful!");
        }
    }
}
๐ Output Example (Console)
When you visit /api/test, the console logs:
โฑ๏ธ [7/28/2025 12:34:56 PM] Request GET /api/test took 201ms
๐จ Example 2: Inject a Custom Header into Every Response
โจ Purpose:
Add a custom header to every outgoing HTTP response.
Middleware/CustomHeaderMiddleware.cs
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace CustomMiddlewareDemo.Middleware
{
    public class CustomHeaderMiddleware
    {
        private readonly RequestDelegate _next;
        public CustomHeaderMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            context.Response.OnStarting(() =>
            {
                context.Response.Headers.Add("X-Powered-By", "ASP.NET Core Custom Middleware");
                return Task.CompletedTask;
            });
            await _next(context);
        }
    }
}
Register it in Program.cs:
app.UseMiddleware<CustomHeaderMiddleware>();
Now every HTTP response will include:
X-Powered-By: ASP.NET Core Custom Middleware
โ
 Best Practices
	
		
			| Practice | Why it Matters | 
	
	
		
			| Keep middleware small and focused | Easier to test, reuse, and maintain | 
		
			| Avoid blocking code ( Thread.Sleep) | Use await Task.Delay()instead for async code | 
		
			| Handle exceptions gracefully | Prevent application crashes and improve logging | 
		
			| Use dependency injection | Middleware can use services like logging, DB, etc. | 
	
๐ Summary
	
		
			| Task | Middleware | 
	
	
		
			| Measure request time | RequestTimingMiddleware | 
		
			| Add custom headers | CustomHeaderMiddleware | 
		
			| Log request/response | Custom logging middleware | 
		
			| Handle errors globally | Exception-handling middleware | 
	
๐งช Want to Go Further?
	- 
	Try building middleware for JWT validation 
- 
	Create a centralized exception handling middleware 
- 
	Write middleware that reads/writes request bodies