Custom Middleware and the Request Pipeline in ASP.NET Core
1. Introduction
The ASP.NET Core request pipeline consists of a sequence of delegates, called Middleware, that are invoked one after another to handle HTTP requests and responses. Understanding how to create and order these components is essential for building efficient and maintainable web applications.
2. How the Pipeline Works
Each middleware component:
- Receives an
HttpContext. - Can perform logic before the next middleware is called.
- Decides whether to pass the request to the next component in the chain (
next()) or “short-circuit” the pipeline (e.g., if a user is unauthorized). - Can perform logic after the next middleware has finished.
3. Creating Custom Middleware
There are two primary ways to create middleware: Inline (using app.Use) and Class-based.
Class-Based Middleware
This is the preferred approach for complex logic and reusability.
public class RequestLoggingMiddleware(RequestDelegate next)
{
public async Task InvokeAsync(HttpContext context)
{
// 1. Logic before the next middleware
Console.WriteLine($"Request: {context.Request.Method} {context.Request.Path}");
// 2. Call the next middleware
await next(context);
// 3. Logic after the next middleware
Console.WriteLine($"Response Status: {context.Response.StatusCode}");
}
}
Registering the Middleware
In Program.cs:
app.UseMiddleware<RequestLoggingMiddleware>();
4. Important Considerations: Ordering
The order in which you register middleware matters immensely. Middleware registered first is executed first on the way in, and last on the way out.
Typical Order:
- ExceptionHandler: Should be first to catch errors from everything that follows.
- HSTS / HTTPS Redirection.
- Static Files.
- Routing.
- CORS.
- Authentication.
- Authorization.
- Custom Business Middleware.
- Endpoints (Controllers/Minimal APIs).
5. Middleware vs. Action Filters
A common question is: “Should I use Middleware or an Action Filter?”
- Use Middleware for cross-cutting concerns that don’t need access to MVC-specific features (like
ModelStateor specific Controller actions). Examples: Logging, Image resizing, Global error handling. - Use Action Filters when you need access to the execution context of a specific controller action. Examples: Validation, specific caching for one endpoint.
6. Best Practices
- Use Extension Methods: Wrap your middleware registration in an extension method for a cleaner
Program.cs.public static class MiddlewareExtensions { public static IApplicationBuilder UseRequestLogger(this IApplicationBuilder builder) => builder.UseMiddleware<RequestLoggingMiddleware>(); } - Avoid Business Logic: Middleware should focus on infrastructure and cross-cutting concerns. Keep business logic in Services.
- Watch for Performance: Since middleware runs on every request, keep it lightweight. Avoid heavy database calls inside
InvokeAsync.
7. Conclusion
Custom middleware is a powerful tool for decorating your request pipeline with necessary features without cluttering your controllers. By mastering the request pipeline, you gain full control over how your .NET web applications process data and respond to clients.
Leave a comment