Introduction
ASP.NET Core middleware forms a pipeline where each component processes the request in the order it was registered. The order is critical: authentication must come before authorization, exception handling must be first, and endpoint routing must be in the correct position. Getting the order wrong leads to silent failures where middleware does not execute as expected.
Symptoms
- Authorization allows unauthenticated requests
- Exception handler does not catch errors from other middleware
- Static files not served even with
UseStaticFiles - CORS preflight requests fail
- Authentication cookies not set
UseRoutingandUseEndpointsmismatch errors
Example incorrect ordering:
``csharp
// WRONG order
app.UseAuthorization(); // Runs BEFORE authentication - meaningless
app.UseAuthentication(); // Should be FIRST
app.UseExceptionHandler("/error"); // Should be first, not third
app.UseStaticFiles(); // Should be before routing
Common Causes
- Adding new middleware without considering its position in the pipeline
- Misunderstanding the request/response flow (middleware order is reverse for response)
- Mixing up
UseRoutingandUseEndpointswithUseAuthorization - Placing exception handling after the middleware that might throw
- Not understanding that
UseStaticFilesshort-circuits the pipeline
Step-by-Step Fix
- 1.Use the correct default middleware order:
- 2.```csharp
- 3.var app = builder.Build();
// 1. Exception handling MUST be first if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); }
// 2. HTTPS redirection (before other middleware) app.UseHttpsRedirection();
// 3. Static files (short-circuits for static content) app.UseStaticFiles();
// 4. Routing (determines which endpoint handles the request) app.UseRouting();
// 5. CORS (must be after UseRouting) app.UseCors("DefaultPolicy");
// 6. Authentication (must be before Authorization) app.UseAuthentication();
// 7. Authorization (must be after Authentication) app.UseAuthorization();
// 8. Custom middleware app.UseMiddleware<RequestLoggingMiddleware>();
// 9. Endpoint execution (must be last) app.MapControllers(); app.MapRazorPages(); ```
- 1.Understand the short-circuit pattern:
- 2.```csharp
- 3.// StaticFiles short-circuits: if it finds the file, it does NOT
- 4.// call the next middleware. This is why it must come before
- 5.// authentication if you want unauthenticated static file access.
- 6.app.UseStaticFiles(); // Serves wwwroot files, short-circuits
- 7.app.UseAuthentication(); // Only runs for non-static-file requests
- 8.app.UseAuthorization(); // Only runs for non-static-file requests
- 9.
` - 10.Create a custom middleware that respects the pipeline:
- 11.```csharp
- 12.public class RequestLoggingMiddleware
- 13.{
- 14.private readonly RequestDelegate _next;
- 15.private readonly ILogger<RequestLoggingMiddleware> _logger;
public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger) { _next = next; _logger = logger; }
public async Task InvokeAsync(HttpContext context) { var sw = Stopwatch.StartNew(); try { await _next(context); // Process request } finally { sw.Stop(); _logger.LogInformation( "{Method} {Path} responded {StatusCode} in {Elapsed}ms", context.Request.Method, context.Request.Path, context.Response.StatusCode, sw.ElapsedMilliseconds); } } } ```
- 1.Debug middleware execution order:
- 2.```csharp
- 3.// Add this temporary middleware at each position to trace execution
- 4.app.Use(async (context, next) =>
- 5.{
- 6.Console.WriteLine($"[1] Before: {context.Request.Path}");
- 7.await next();
- 8.Console.WriteLine($"[1] After: {context.Response.StatusCode}");
- 9.});
// Add similar at each position to see the full pipeline flow ```
Prevention
- Document the middleware order in your Program.cs with numbered comments
- Use
WebApplicationFactoryintegration tests to verify middleware behavior - Test CORS, authentication, and authorization independently
- Add a middleware ordering checklist to your code review template
- Use the
Microsoft.AspNetCore.Diagnosticspackage for standard error handling - Never place
UseExceptionHandlerafter middleware that might throw