← Back to Blog
Design Patterns

Middleware Pattern in ASP.NET Core: Building a Request Pipeline

nemostormJanuary 11, 202613 min read

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
This creates a powerful, flexible architecture for handling cross-cutting concerns like authentication, logging, error handling, and custom request processing.

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

csharp
// 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
Logging & Monitoring
  • Log request/response details
  • Track performance metrics
  • Monitor application health
Error Handling
  • Catch and handle exceptions
  • Return consistent error responses
  • Log error details
Request Modification
  • Add custom headers
  • Rewrite URLs
  • Parse custom data formats
Caching
  • 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
Keep It Focused
  • Each middleware should have a single responsibility
  • Avoid complex business logic in middleware
  • Use dependency injection for services
Performance Considerations
  • Minimize work in the request path
  • Use async/await properly
  • Consider short-circuiting when appropriate
Testing
  • 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.
Use Filters when:
  • 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.