Middleware Pattern in ASP.NET Core: Building a Request Pipeline
Middleware Pattern in ASP.NET Core: Building a Request Pipeline
What is the Middleware Pattern?
The Middleware Pattern in ASP.NET Core is a design pattern that allows you to create a pipeline of components that process HTTP requests and responses. Each middleware component can:
- Perform operations before the next component in the pipeline
- Pass the request to the next middleware component
- Perform operations after the next component executes
- Short-circuit the pipeline by not calling the next middleware
The ASP.NET Core Request Pipeline
When an HTTP request arrives, it flows through the middleware pipeline. Each middleware component decides whether to:
1. Process and pass: Handle the request and call the next middleware 2. Short-circuit: Handle the request and return a response without calling next 3. Pass through: Simply call the next middleware without processing
The response then flows back through the pipeline in reverse order, allowing each middleware to process the response.
Building Custom Middleware
// Simple inline middleware in Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Inline middleware
app.Use(async (context, next) =>
{
// Before next middleware
Console.WriteLine($"Request: {context.Request.Method} {context.Request.Path}");
await next(context); // Call next middleware
// After next middleware
Console.WriteLine($"Response: {context.Response.StatusCode}");
});
app.MapGet("/", () => "Hello World!");
app.Run();How the Pipeline Works
The middleware pipeline executes in the order you register components in Program.cs:
1. Request Phase: Middleware executes top-to-bottom 2. Response Phase: Middleware executes bottom-to-top
Example Flow:
Request → Logging → Auth → Exception Handler → Endpoint → Response ↓ ↓ ↓ ↓ ↑ Response ← Logging ← Auth ← Exception Handler ← Endpoint ←
Common Middleware Use Cases
Authentication & Authorization
- Validate JWT tokens
- Check user permissions
- Enforce security policies
- Log request/response details
- Track performance metrics
- Monitor application health
- Catch and handle exceptions
- Return consistent error responses
- Log error details
- Add custom headers
- Rewrite URLs
- Parse custom data formats
- Implement response caching
- Cache validation
- Set cache headers
Best Practices
Order Matters
- Exception handling should be early in the pipeline
- Authentication before authorization
- Logging early to capture all requests
- Each middleware should have a single responsibility
- Avoid complex business logic in middleware
- Use dependency injection for services
- Minimize work in the request path
- Use async/await properly
- Consider short-circuiting when appropriate
- Unit test middleware in isolation
- Mock HttpContext and RequestDelegate
- Test both success and error paths
Middleware vs. Filters
ASP.NET Core also has Filters for MVC/API actions. Here's when to use each:
Use Middleware when:
- Logic applies to all requests (not just MVC endpoints)
- You need to short-circuit the pipeline
- Processing static files, SignalR, etc.
- Logic is specific to MVC/API actions
- You need access to model binding
- Working with action results
Conclusion
The Middleware Pattern in ASP.NET Core provides a clean, modular way to build request processing pipelines. By understanding how middleware works and following best practices, you can create maintainable, performant web applications with clearly separated concerns.
Whether you're adding authentication, logging, custom headers, or any cross-cutting functionality, middleware gives you the tools to build it elegantly and efficiently.