ASP.NET Core ships with a set of built-in middleware components that handle the most common web application concerns. Each must be registered in a specific order because they depend on each other — UseAuthentication must come before UseAuthorization, and UseRouting must come before both. Knowing what each built-in middleware does and where it belongs in the pipeline is a practical production skill that prevents entire categories of security and correctness bugs.
Standard Middleware Pipeline for a Web API
var app = builder.Build();
// ── 1. Exception handling — first so it catches all downstream exceptions ──
if (app.Environment.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseExceptionHandler(); // maps exceptions to ProblemDetails
// ── 2. HSTS — adds Strict-Transport-Security header (production only) ─────
if (!app.Environment.IsDevelopment())
app.UseHsts();
// ── 3. HTTPS redirection — redirect HTTP to HTTPS ─────────────────────────
app.UseHttpsRedirection();
// ── 4. Static files — serve from wwwroot before routing (no auth required) ─
app.UseStaticFiles();
// ── 5. Routing — must be before CORS, auth, and endpoints ─────────────────
app.UseRouting();
// ── 6. CORS — must be after routing, before auth ──────────────────────────
app.UseCors("AllowAngularDev");
// ── 7. Authentication — must be before authorization ──────────────────────
app.UseAuthentication();
// ── 8. Authorization — must be after authentication ───────────────────────
app.UseAuthorization();
// ── 9. Custom middleware (runs in request/endpoint context) ───────────────
app.UseMiddleware<CorrelationIdMiddleware>();
// ── 10. Endpoint mapping — terminal; must be last ─────────────────────────
app.MapControllers();
app.MapHealthChecks("/health");
UseRouting() and UseEndpoints() (or their .NET 6+ equivalent MapControllers()) bookend the auth middleware. Route matching happens in UseRouting(), which makes the matched endpoint available in HttpContext.GetEndpoint(). Auth middleware reads the endpoint’s metadata (e.g., [Authorize] attributes) to decide whether to challenge or allow the request. Without UseRouting() first, auth middleware cannot see the endpoint metadata and cannot enforce endpoint-level policies.Program.cs with named policies to clearly separate development and production origins. Register multiple policies: builder.Services.AddCors(opts => { opts.AddPolicy("AllowAngularDev", p => p.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader()); opts.AddPolicy("AllowProd", p => p.WithOrigins("https://blogapp.com").AllowAnyMethod().AllowAnyHeader()); }). Apply the correct policy in middleware with app.UseCors(app.Environment.IsDevelopment() ? "AllowAngularDev" : "AllowProd").AllowAnyOrigin().AllowCredentials() together in CORS policy — this is explicitly rejected by the CORS specification and will throw an exception. If you need credentials (cookies, Authorization headers), you must specify exact allowed origins with WithOrigins("https://blogapp.com"). The Angular client in Part 6 uses JWT bearer tokens (not cookies), so AllowCredentials() is generally not needed — AllowAnyHeader() to allow the Authorization header is sufficient.CORS Configuration for Angular
// ── Register CORS policies in Program.cs build phase ─────────────────────
builder.Services.AddCors(options =>
{
options.AddPolicy("Development", policy =>
policy
.WithOrigins("http://localhost:4200") // Angular dev server
.AllowAnyMethod()
.AllowAnyHeader());
options.AddPolicy("Production", policy =>
policy
.WithOrigins(
"https://blogapp.com",
"https://www.blogapp.com")
.WithMethods("GET", "POST", "PUT", "DELETE", "PATCH")
.WithHeaders("Authorization", "Content-Type", "X-Correlation-Id")
.SetPreflightMaxAge(TimeSpan.FromHours(24))); // cache preflight responses
});
// ── Apply in pipeline phase ───────────────────────────────────────────────
var corsPolicy = app.Environment.IsDevelopment() ? "Development" : "Production";
app.UseCors(corsPolicy);
Common Mistakes
Mistake 1 — UseAuthorization before UseAuthentication (authorization without identity)
❌ Wrong — authorization runs before the user is authenticated; all [Authorize] requests fail:
app.UseAuthorization(); // user identity not established yet
app.UseAuthentication(); // too late!
✅ Correct — UseAuthentication must always precede UseAuthorization.
Mistake 2 — UseCors after MapControllers (CORS headers never added)
❌ Wrong — controllers respond before CORS middleware runs; no CORS headers in response.
✅ Correct — UseCors must be before MapControllers (and after UseRouting).