Table of Contents
TL;DR
- Repeating logic across controllers (like logging or header checks)? Move it to middleware.
- Modifying requests/responses in controllers? Middleware handles that cleanly and early.
- Your services use
HttpContext
? Extract that logic into middleware for cleaner, testable code.
If your services are bloated or you’re duplicating logic across controllers, middleware might be the extension point you actually need. Many developers reach for filters or services first, when middleware would’ve been faster, simpler, and more maintainable.
Sign #1: Repeating Code Across Controllers?
The Problem: You’re seeing identical logging, CORS handling, header validation, or request sanitization code across multiple controllers.
// Instead of this in every controller
[HttpGet]
public async Task<IActionResult> GetUsers()
{
_logger.LogInformation("Request received from {IP}", Request.HttpContext.Connection.RemoteIpAddress);
if (!Request.Headers.ContainsKey("X-API-Version"))
return BadRequest("API version required");
// actual logic here
}
Why Middleware Helps: Middleware centralizes repeated logic at the pipeline level, executing once before requests reach your controllers. This reduces code duplication and improves performance by handling cross-cutting concerns upstream.
Sign #2: You’re Modifying Requests or Responses Frequently
The Problem: Controllers are injecting headers, rewriting URLs, or transforming request bodies before processing business logic.
Why Middleware Helps: ASP.NET Core middleware is purpose-built for request/response interception and transformation. Moving this logic to middleware keeps your action methods focused on business logic while handling framework concerns efficiently.
public class TenantResolutionMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var tenantId = context.Request.Headers["X-Tenant-ID"].FirstOrDefault();
context.Items["TenantId"] = tenantId ?? "default";
await next(context);
}
}
Sign #3: Your Services Depend on HttpContext
The Problem: Business services are injecting HttpContext
, accessing Request.Headers
, or checking environment details directly.
Why Middleware Helps: Middleware isolates framework concerns from business logic. Extract tenant information, authentication tokens, or culture settings in middleware, then pass clean data to your services through scoped services or HttpContext.Items
, keeping services clean.
This pattern improves testability, your services no longer need mock HTTP contexts for unit tests.
Performance Impact
Based on profiling real production apps handling 10,000+ requests per minute, moving cross-cutting concerns to middleware typically reduces controller execution time by 15-20% and simplifies debugging through centralized logging.
Bottom Line: Middleware isn’t just a pipeline detail, it’s an architectural tool for isolating cross-cutting concerns and maintaining clean, testable code.