Use, Run, Map, short-circuit, ordering, custom middleware
Every HTTP request in ASP.NET Core passes through a pipeline of middleware components before reaching your controller.
Understanding this pipeline is essential — it's where logging, authentication, exception handling, CORS, routing, and response caching all live.
What Is Middleware?
Middleware is software assembled into a pipeline to handle requests and responses.
Each middleware component:
- Receives the incoming
HttpContext - Optionally does something with the request
- Calls the next middleware in the chain
- Optionally does something with the response on the way back
Request → [Logging] → [Auth] → [CORS] → [Routing] → [Handler]
Response ← [Logging] ← [Auth] ← [CORS] ← [Routing] ← [Handler]
The Three Methods: Use, Run, Map
Use — calls the next component
app.Use(async (context, next) =>
{
Console.WriteLine("Before next middleware");
await next(context);
Console.WriteLine("After next middleware");
});
Run — terminal, does NOT call next
app.Run(async context =>
{
await context.Response.WriteAsync("Hello — pipeline stops here");
});
Map — branches based on path
app.Map("/health", branch =>
{
branch.Run(async context =>
{
await context.Response.WriteAsync("Healthy");
});
});
Ordering Matters — A Lot
The order you register middleware is the order it runs.
app.UseExceptionHandler(); // 1 — catch all unhandled exceptions first
app.UseHttpsRedirection(); // 2
app.UseCors(); // 3
app.UseAuthentication(); // 4 — who are you?
app.UseAuthorization(); // 5 — what can you do?
app.UseRateLimiter(); // 6
app.MapControllers(); // 7 — route to controller
Getting the order wrong causes real bugs. Authentication before exception handling means auth errors won't be caught cleanly.
Short-Circuiting the Pipeline
Middleware can stop the request from proceeding further.
app.Use(async (context, next) =>
{
if (!context.Request.Headers.ContainsKey("X-API-Key"))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Missing API key");
return; // ← Does NOT call next — pipeline stops here
}
await next(context);
});
This is how authentication middleware works — validate the token, let through or short-circuit with 401.
Building Custom Middleware
Inline (simple cases)
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Custom-Header", "MyApp");
await next(context);
});
Class-based (preferred for complex 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 stopwatch = Stopwatch.StartNew();
await _next(context);
stopwatch.Stop();
_logger.LogInformation(
"Request {Method} {Path} completed in {ElapsedMs}ms",
context.Request.Method,
context.Request.Path,
stopwatch.ElapsedMilliseconds);
}
}
// Extension method
public static class RequestTimingMiddlewareExtensions
{
public static IApplicationBuilder UseRequestTiming(this IApplicationBuilder app)
=> app.UseMiddleware<RequestTimingMiddleware>();
}
// Usage
app.UseRequestTiming();
Real-World Scenarios
Logging every request and response
app.Use(async (context, next) =>
{
_logger.LogInformation("Incoming: {Method} {Path}",
context.Request.Method, context.Request.Path);
await next(context);
_logger.LogInformation("Outgoing: {StatusCode}",
context.Response.StatusCode);
});
Adding a correlation ID to every response
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Correlation-Id", Guid.NewGuid().ToString());
await next(context);
});
Interview-Ready Summary
- Middleware forms a bidirectional pipeline — request goes in, response comes back
-
Use= calls next,Run= terminal,Map= branch by path - Order of registration = order of execution
- Short-circuiting stops the pipeline — used by auth, rate limiting, validation
- Custom middleware goes in a class implementing
InvokeAsync(HttpContext) - Exception handling middleware should be first (outermost) so it catches everything
A strong interview answer:
"ASP.NET Core middleware forms a pipeline where each component can process the request before passing it to the next, and then process the response on the way back. The order matters — exception handling goes first, then CORS, then authentication, then routing. You can short-circuit the pipeline at any point, which is how auth middleware rejects unauthorised requests."
Top comments (0)