Before your Controller's Action Method ever fires, an HTTP request must run a gauntlet of security checks, routing algorithms, and formatters. Understanding the ASP.NET Core Request Lifecycle and mastering the Middleware Pipeline lets you manipulate requests globally, inject custom security headers, and diagnose where exactly a request is failing.
Middleware is any piece of software that is assembled into the application pipeline to handle requests and responses. Each component chooses whether to pass the request on to the next component in the pipeline and can perform work before and after the next component is invoked.
HTTP GET /Dashboard
β
[Exception Handling Middleware] (Catches anything breaking below)
β
[HTTPS Redirection Middleware] (Forces Secure Connection)
β
[Static Files Middleware] (Checks if requesting /css/style.css, if so, returns early)
β
[Routing Middleware] (Maps /Dashboard to the correct Controller class)
β
[Authentication Middleware] (Reads cookies, establishes User Claims)
β
[Authorization Middleware] (Validates if User has Roles to see Dashboard)
β
[Endpoint Middleware (MVC)] ---> [Filters] ---> [DashboardController.Index()]
Middleware is registered in Program.cs via app.Use...() extension methods. The order in which they are added dictates the exact order they execute on incoming requests.
var app = builder.Build();
// 1. Catches exceptions from everything below it first.
app.UseExceptionHandler("/Home/Error");
// 2. Halts the request IMMEDIATELY if it asks for an image/css.
// (Saves Routing & Auth from doing unnecessary work)
app.UseStaticFiles();
// 3. Figures out WHERE the request is going
app.UseRouting();
// 4. Figures out WHO the user is
app.UseAuthentication();
// 5. Figures out if WHO is allowed to go WHERE
// (CRITICAL: Must be placed AFTER UseRouting and UseAuthentication)
app.UseAuthorization();
// 6. Executes the actual MVC Controller logic
app.MapControllers();
app.Run();
You can write custom middleware to inject global logic. For example, logging response times or rejecting access from banned IP addresses.
// Simple logging middleware directly in Program.cs
app.Use(async (context, next) =>
{
var stopwatch = Stopwatch.StartNew();
// 1. Do work BEFORE sending it down the pipeline
Console.WriteLine($"Request started: {context.Request.Path}");
// 2. Await the NEXT middleware in the chain (this eventually hits your Controller)
await next.Invoke();
// 3. Do work AFTER the pipeline returns (the Response is now generated)
stopwatch.Stop();
Console.WriteLine($"Request finished in {stopwatch.ElapsedMilliseconds} ms");
});
// In custom class: RequiredHeaderMiddleware.cs
public class RequiredHeaderMiddleware
{
private readonly RequestDelegate _next;
// The runtime injects the "next" component parameter
public RequiredHeaderMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context)
{
// Example: Reject API requests missing a custom Tenant ID header
if (!context.Request.Headers.ContainsKey("X-Tenant-ID"))
{
context.Response.StatusCode = 400; // Bad Request
await context.Response.WriteAsync("X-Tenant-ID header is missing.");
// Short-circuit: By NOT calling _next.Invoke(), the pipeline immediately
// halts and returns the 400 back to the browser. Controllers are never hit.
return;
}
// Valid: Proceed deeper into the pipeline
await _next(context);
}
}
// In Program.cs
app.UseMiddleware<RequiredHeaderMiddleware>();
app.Use(): Adds middleware to the pipeline. Can pass execution to the next component or choose to short-circuit.app.Map(): Branches the pipeline based on the URL path. (e.g., app.Map("/api", apiApp => ... ) creates a separate pipeline just for API paths).app.Run(): A Terminal Middleware. It executes and immediately starts winding the response back up out of the pipeline. It does not possess a next parameter.Q: "What is the difference between an Action Filter and Middleware? When would you use one over the other?"
Architect Answer: "Middleware wraps the entire HTTP request globally, independent of the routing framework. It handles infrastructural concerns like CORS, global exception catching, forcing HTTPS, and basic Authentication. It exists outside of MVC. **Action Filters**, on the other hand, are strictly part of the MVC/API routing pipeline. They execute *after* the framework has selected a Controller and performed Model Binding. You use an Action Filter when your logic requires context about the MVC execution itselfβlike inspecting the `ModelState.IsValid` dictionary, accessing the parsed Action Parameters, or modifying the specific ViewResult returned. A general rule: If it applies to the raw HTTP Network Protocol, use Middleware. If it applies to your Business Domain or Controller routing logic, use a Filter."