ASP.NET Core Middleware
Middleware components form the request processing pipeline in ASP.NET Core. Each middleware component has access to the request and response objects and can perform actions such as:
- Executing code before or after the rest of the pipeline is invoked.
- Short-circuiting the pipeline execution.
- Calling the next middleware in the pipeline.
- Selecting the next middleware to execute.
Understanding the Middleware Pipeline
ASP.NET Core applications use a pipeline of middleware components to handle incoming HTTP requests. The pipeline is configured in the Startup.cs
(or Program.cs
in .NET 6+) file using the UseMiddleware<T>
extension method or by calling specific extension methods like UseStaticFiles()
, UseRouting()
, and UseEndpoints()
.
How Middleware Works
Each middleware component is responsible for a specific task. When a request arrives, it passes through the middleware components in the order they are configured. The first middleware component can:
- Perform operations on the request.
- Optionally, invoke the next middleware component in the pipeline by calling the
next()
delegate. - If
next()
is called, perform operations on the response.
If a middleware component doesn't call the next delegate, the request processing stops at that component. This is known as short-circuiting.
Common Middleware Examples
Static Files Middleware
Serves static files (HTML, CSS, JavaScript, images) from the web root. You typically add this early in the pipeline.
app.UseStaticFiles();
Routing Middleware
Processes incoming requests to match them with an endpoint. This is crucial for MVC and Razor Pages.
app.UseRouting();
Authentication Middleware
Handles authentication logic, such as validating credentials and setting the principal for the request.
app.UseAuthentication();
Authorization Middleware
Enforces authorization policies based on the authenticated user's identity.
app.UseAuthorization();
Endpoints Middleware
Executes the matched endpoint handler (e.g., MVC controller action, Razor Page handler).
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapGet("/", async context => await context.Response.WriteAsync("Hello World!"));
});
Creating Custom Middleware
You can create your own middleware to implement custom logic. A middleware component can be implemented as:
- A class with an
InvokeAsync
method. - A class with a constructor that accepts
RequestDelegate
and can be instantiated by the framework.
Example: Custom Logging Middleware
This middleware logs the request path before calling the next middleware and logs the response status code after.
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
Console.WriteLine($"--> Request starting: {context.Request.Method} {context.Request.Path}");
await _next(context);
Console.WriteLine($"<-- Response finished: {context.Response.StatusCode}");
}
}
// In Startup.cs or Program.cs:
// app.UseMiddleware();
Middleware Order
The middleware pipeline is executed in the order in which it is configured. Consider the following order:
UseStaticFiles()
(to serve static content directly)UseRouting()
(to match the request to an endpoint)UseAuthentication()
(to authenticate the user)UseAuthorization()
(to authorize the user for the endpoint)UseEndpoints()
(to execute the endpoint handler)
Short-Circuiting the Pipeline
Some middleware can prevent further processing of the request if certain conditions are met. For example, if a user is not authorized, the authorization middleware might short-circuit the pipeline and return an unauthorized response.