Mastering Middlewares in ASP.NET Core: Everything You Need to Know to Build Efficient Pipelines
Within the lifecycle of an ASP.NET application, the request pipeline undergoes a sequential process where middlewares are present. They can execute commands and validations during the pipeline before reaching the final handler (controller, endpoint, or handler) and before returning the response to the client.
How Does It Work?
When a request reaches the application, the first middleware in the pipeline is called. From there, a sequence is followed until reaching the endpoint that will perform the required operations. After executing and returning the response, it travels back through the pipeline, passing from the last middleware executed to the first one, where the response is sent to the client.
In summary, when a request arrives, the application “stacks” the middlewares, and when returning the response, it “unstacks” them. This is known as LIFO (Last In, First Out), which represents a stack.
Common Use Cases
In ASP.NET applications, there are already several middlewares designed for execution.
Controller Mapping Middleware:
- Used to map controllers to the API endpoints that will handle requests.
Razor Pages Control:
- Used to define routes and load Razor Pages.
Authentication and Authorization:
- Validate tokens or cookies.
- Verify access permissions.
CORS (Cross-Origin Resource Sharing):
- Manage cross-origin sharing policies.
Cache:
- Store frequently requested responses to improve performance.
Logging and Monitoring:
- Record request and response information for diagnostics.
Error Handling:
- Capture exceptions and return user-friendly messages.
- Record failure logs.
Practical Example
Creating Middleware:
public class MyMiddleware
{
private readonly RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Logic before calling the next middleware
Console.WriteLine($"Receiving request: {context.Request.Path}");
// Calling the next middleware in the pipeline
await _next(context);
// Logic after the next middleware processes the response
Console.WriteLine($"Sending response: {context.Response.StatusCode}");
}
}
Registering the Middleware in the Pipeline:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<MyMiddleware>(); // Registering the middleware
app.UseAuthentication(); // Internal middleware for authentication
app.UseAuthorization(); // Internal middleware for authorization
app.UseRouting(); // Internal middleware for routing
app.UseEndpoints(endpoints => endpoints.MapControllers()); // Mapping endpoints
}
}
Built-in Middlewares in ASP.NET Core
ASP.NET Core provides several ready-to-use middlewares for various functionalities. Here are some:
- UseAuthentication: Validates user credentials.
- UseAuthorization: Ensures users have permission to access resources.
- UseCors: Configures CORS policies.
- UseStaticFiles: Serves static files such as images, CSS, and JavaScript.
- UseEndpoints: Configures route endpoints.
Order Matters
In the example above, if the custom middleware is placed after app.UseAuthorization
, it will only execute after authentication and authorization checks are completed.
There is a convention for best practices that also follows logical reasoning when defining the execution order:
- Middlewares that process requests and responses should come first (e.g., logging, exception handling).
- Middlewares that restrict access should be placed before middlewares that expose endpoints.
- Middlewares that configure routing and endpoints should be the last.

Advantages
- Modularity: Logic is isolated in reusable components.
- Extensibility: Allows creating custom middlewares for specific needs.
- Maintainability: Facilitates separation of responsibilities within the pipeline.
- Performance: Efficiently processes requests, avoiding redundant logic.
Considerations When Configuring Custom Middlewares
- Incorrect Order: Can cause unexpected behavior, such as ignored permissions or unmapped routes.
- Unnecessary Blocking: Failing to call the next middleware (
await next()
) can improperly interrupt the flow. - Overhead: Adding too many middlewares can impact performance.
Middlewares are crucial in designing pipelines for modern web applications, providing flexibility, control, and scalability. Understanding how they work and applying them correctly is essential for building robust and high-performance applications.