Section 4 of 5

Middleware Ordering

🎯 What You'll Learn

  • Why middleware order matters
  • The recommended middleware order
  • Common ordering mistakes
  • How order affects behavior
  • Special ordering considerations
  • Debugging order issues
  • InvenTrack pipeline organization

Why Order Matters

Middleware executes in the order it's registered. The order determines:

  • What runs first: Early middleware sees all requests
  • What can short-circuit: Early middleware can stop the pipeline
  • What modifies requests/responses: Order affects transformations
  • Security: Authentication must come before authorization
⚠️ Wrong Order = Broken App

Incorrect middleware order can cause:

  • Security vulnerabilities (authorization before authentication)
  • 404 errors (routing after static files)
  • Missing CORS headers
  • Unhandled exceptions

Recommended Middleware Order

Microsoft recommends this order for ASP.NET Core applications:

Recommended Order C#
var app = builder.Build();

// 1. Exception Handling (catch all errors)
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/error");
    app.UseHsts();
}

// 2. HTTPS Redirection (force HTTPS early)
app.UseHttpsRedirection();

// 3. Static Files (short-circuit for static content)
app.UseStaticFiles();

// 4. Routing (match endpoints)
app.UseRouting();

// 5. CORS (after routing, before auth)
app.UseCors();

// 6. Authentication (identify user)
app.UseAuthentication();

// 7. Authorization (check permissions)
app.UseAuthorization();

// 8. Custom Middleware (business logic)
app.UseRequestTiming();

// 9. Endpoints (controllers, minimal APIs)
app.MapControllers();

app.Run();

Visual Representation

Pipeline Flow Text
Request
  ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 1. Exception Handler            β”‚ ← Catches all errors
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 2. HTTPS Redirection            β”‚ ← Redirect HTTP β†’ HTTPS
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 3. Static Files                 β”‚ ← Serve CSS/JS/images
β”‚                                 β”‚   (may short-circuit)
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 4. Routing                      β”‚ ← Match request to endpoint
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 5. CORS                         β”‚ ← Add CORS headers
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 6. Authentication               β”‚ ← Identify user
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 7. Authorization                β”‚ ← Check permissions
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 8. Custom Middleware            β”‚ ← Your logic
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 9. Endpoint                     β”‚ ← Controller/API
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  ↓
Response

Critical Ordering Rules

1. Exception Handler First

βœ… Correct C#
app.UseExceptionHandler("/error"); // First!
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
❌ Wrong C#
app.UseAuthentication();
app.UseExceptionHandler("/error"); // Too late!
app.MapControllers();

// Problem: Exceptions in authentication won't be caught!

2. Authentication Before Authorization

βœ… Correct C#
app.UseAuthentication(); // First: Identify user
app.UseAuthorization();  // Then: Check permissions
❌ Wrong C#
app.UseAuthorization();  // Can't check permissions without knowing who the user is!
app.UseAuthentication();

3. CORS After Routing

βœ… Correct C#
app.UseRouting();
app.UseCors(); // After routing
app.UseAuthentication();
❌ Wrong C#
app.UseCors(); // Before routing - may not work!
app.UseRouting();

4. Static Files Early

βœ… Correct C#
app.UseStaticFiles(); // Early - short-circuit for static files
app.UseRouting();
app.UseAuthentication();
app.MapControllers();
❌ Wrong C#
app.UseRouting();
app.UseAuthentication();
app.MapControllers();
app.UseStaticFiles(); // Too late - static files won't be served!

Common Mistakes

Mistake 1: Authorization Before Authentication

Problem C#
app.UseAuthorization();
app.UseAuthentication(); // Wrong order!

// Result: Authorization fails because user is not identified yet

Mistake 2: Custom Middleware Before Authentication

Problem C#
app.Use(async (context, next) =>
{
    var userId = context.User.FindFirst("sub")?.Value; // null!
    await next();
});

app.UseAuthentication(); // User not set yet!

Mistake 3: Exception Handler Too Late

Problem C#
app.UseAuthentication();
app.UseExceptionHandler("/error"); // Too late!

// Result: Exceptions in authentication middleware are not caught

Special Considerations

Response Compression

Compression Placement C#
app.UseResponseCompression(); // Before static files
app.UseStaticFiles();

// Why? Static files can be compressed too

Response Caching

Caching Placement C#
app.UseResponseCaching(); // Before authentication
app.UseAuthentication();

// Why? Cached responses can be served without authentication

Session Middleware

Session Placement C#
app.UseRouting();
app.UseSession(); // After routing, before endpoints
app.MapControllers();

Complete InvenTrack Pipeline

Program.cs (Production-Ready) C#
var builder = WebApplication.CreateBuilder(args);

// Configure services
builder.Services.AddControllers();
builder.Services.AddDbContext<InvenTrackDbContext>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer();
builder.Services.AddAuthorization();
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowFrontend", policy =>
        policy.WithOrigins("https://inventrackapp.com")
              .AllowAnyMethod()
              .AllowAnyHeader());
});
builder.Services.AddResponseCompression();

var app = builder.Build();

// ============================================
// MIDDLEWARE PIPELINE (ORDER MATTERS!)
// ============================================

// 1. Exception Handling (FIRST - catch all errors)
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/error");
    app.UseHsts();
}

// 2. HTTPS Redirection
app.UseHttpsRedirection();

// 3. Response Compression (before static files)
app.UseResponseCompression();

// 4. Static Files (early - can short-circuit)
app.UseStaticFiles();

// 5. Routing
app.UseRouting();

// 6. CORS (after routing, before auth)
app.UseCors("AllowFrontend");

// 7. Custom Middleware - Correlation ID
app.UseCorrelationId();

// 8. Custom Middleware - Request Timing
app.UseRequestTiming();

// 9. Authentication (identify user)
app.UseAuthentication();

// 10. Authorization (check permissions)
app.UseAuthorization();

// 11. Custom Middleware - API Key (after auth)
app.UseApiKey();

// 12. Endpoints (LAST)
app.MapControllers();

app.Run();

Why This Order?

Position Middleware Reason
1 Exception Handler Catch all errors from any middleware
2 HTTPS Redirection Force HTTPS before any processing
3 Response Compression Compress all responses (including static files)
4 Static Files Short-circuit early for performance
5 Routing Match request to endpoint
6 CORS Add CORS headers after routing
7-8 Custom (Correlation, Timing) Track requests before auth
9 Authentication Identify user
10 Authorization Check permissions (after auth)
11 Custom (API Key) Additional security after auth
12 Endpoints Execute controller/API logic

Debugging Order Issues

Add Logging Middleware

Debug Middleware Order C#
app.Use(async (context, next) =>
{
    Console.WriteLine($"[1] Before: {context.Request.Path}");
    await next();
    Console.WriteLine($"[1] After: {context.Response.StatusCode}");
});

app.UseAuthentication();

app.Use(async (context, next) =>
{
    Console.WriteLine($"[2] User: {context.User.Identity?.Name ?? "Anonymous"}");
    await next();
});

Check User Identity

Verify Authentication Order C#
app.Use(async (context, next) =>
{
    if (context.User.Identity?.IsAuthenticated == true)
    {
        Console.WriteLine("User is authenticated");
    }
    else
    {
        Console.WriteLine("User is NOT authenticated");
    }
    await next();
});

Key Takeaways

  • Order matters: Middleware executes in registration order
  • Exception handler first: Catch all errors
  • Authentication before authorization: Can't check permissions without knowing the user
  • CORS after routing: Routing must happen first
  • Static files early: Short-circuit for performance
  • Custom middleware placement: Depends on what it needs (user, routing, etc.)
  • Endpoints last: Final destination
  • Wrong order = broken app or security issues
  • Use logging to debug order issues
  • Follow Microsoft's recommended order
🎯 Next Steps

You now understand middleware ordering! In the final section, we'll explore Exception Handling Middlewareβ€”creating robust error handling, custom error pages, logging exceptions, and building a production-ready error handling strategy for InvenTrack.