Introduction
Middleware forms the ASP.NET Core pipeline — each component can inspect, modify, or short-circuit the HTTP request. Like airport security: every passenger (request) passes checkpoints in a fixed order before reaching the gate (controller).
After this article you will
- Understand pipeline order: Exception → HTTPS → Static → Routing → Auth → Endpoints
- Build custom logging and timing middleware
- Short-circuit with Run/Map
- Compare IMiddleware vs convention-based middleware
- Know when middleware beats action filters
Prerequisites
- Article 23 — Dependency Injection
- ShopNest.Web with EF Core from Module 2
Concept deep-dive
Correct order (simplified)
ExceptionHandler → HTTPS → StaticFiles → Routing → CORS
→ Authentication → Authorization → Endpoints
Bug example: UseAuthorization() before UseAuthentication() — user is never authenticated, all [Authorize] fails mysteriously.
// Convention-based middleware
public class RequestTimingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestTimingMiddleware> _logger;
public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var sw = Stopwatch.StartNew();
await _next(context);
sw.Stop();
_logger.LogInformation("{Method} {Path} {Status} {Ms}ms",
context.Request.Method, context.Request.Path,
context.Response.StatusCode, sw.ElapsedMilliseconds);
}
}
// Register
app.UseMiddleware<RequestTimingMiddleware>();
IMiddleware: resolved from DI per request — inject scoped services safely. Inline: app.Use(async (ctx, next) => { ... });
| Middleware | Action filters |
|---|---|
| Entire pipeline, all requests | MVC/API actions only |
| Routing, auth, static files | Model binding, validation, caching per action |
Hands-on — ShopNest API Gateway / Request Logging
- RequestTimingMiddleware — log duration and status code.
- RequestResponseLoggingMiddleware — log body in Development only (mask passwords).
- Map("/health", app => app.Run(async ctx => await ctx.Response.WriteAsync("OK"))).
- Verify order in Program.cs matches diagram above.
Common errors & best practices
- Middleware after MapControllers that never calls next — later middleware skipped.
- Reading request body without EnableBuffering — stream consumed, model binding breaks.
- Heavy logic in middleware for one route — use filters or endpoint metadata instead.
Interview questions
Q: Middleware order?
A: First registered runs first on way in; reverse on way out — auth before authorization.
Q: Short-circuit?
A: Don't call next() — pipeline stops, response sent (e.g. IP block).
Q: Middleware vs filter?
A: Middleware for cross-cutting HTTP concerns; filters for MVC action pipeline.
Summary
- Pipeline order is critical — auth before authorization
- Custom middleware logs ShopNest API gateway traffic
- IMiddleware enables DI in middleware classes
- Use Map/Run for branch pipelines
Previous: Dependency Injection
Next: Configuration in ASP.NET Core
FAQ
Can middleware access DbContext?
Use IMiddleware with scoped injection, not singleton middleware holding DbContext.
Where put rate limiting?
.NET 8 built-in rate limiter middleware before endpoints.