Introduction
Middleware is a software component that processes HTTP requests and responses in the ASP.NET Core pipeline.
Each request passes through a series of middleware components before reaching the final endpoint (like a controller or API). After the endpoint processes the request, the response travels back through the same middleware pipeline.
Request Flow
Client Request
↓
Middleware 1 (Logging)
↓
Middleware 2 (Authentication)
↓
Middleware 3 (Routing)
↓
Controller / API
↑
Response travels back through middleware
Each middleware can:
Inspect or modify the request
Call the next middleware in the pipeline
Modify the response
Stop the pipeline if needed
Basic Middleware Example
Let’s see a simple middleware example.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Use(async (context, next) =>
{
Console.WriteLine("Request started");
await next();
Console.WriteLine("Response completed");
});
app.Run(async context =>
{
await context.Response.WriteAsync("Hello World");
});
app.Run();
Execution Flow
A request arrives.
Middleware prints Request started.
next() passes the request to the next middleware.
Run() sends the response.
Control returns to the previous middleware.
Middleware prints Response completed.
Console Output:
Request started
Response completed
Understanding Use Middleware
Use() middleware is used when we want to perform logic before and after the next middleware.
Example
app.Use(async (context, next) =>
{
Console.WriteLine("Before next middleware");
await next();
Console.WriteLine("After next middleware");
});
Key Characteristics
This is commonly used for:
Logging
Request validation
Performance monitoring
Understanding Run Middleware
Run() is a terminal middleware.
Once Run() executes, the pipeline stops and no other middleware is executed after it.
Example
app.Run(async context =>
{
await context.Response.WriteAsync("Pipeline ended here");
});
If multiple Run() methods are written:
app.Run(async context =>
{
await context.Response.WriteAsync("Hello");
});
app.Run(async context =>
{
await context.Response.WriteAsync("World");
});
Output:
Hello
The second Run() will never execute.
Understanding Map Middleware
Map() is used to branch the middleware pipeline based on URL paths.
Example
app.Map("/admin", adminApp =>
{
adminApp.Run(async context =>
{
await context.Response.WriteAsync("Admin page");
});
});
app.Run(async context =>
{
await context.Response.WriteAsync("Main page");
});
Result
| URL | Response |
|---|
| / | Main page |
| /admin | Admin page |
This approach is useful when we want different middleware pipelines for different routes.
Creating Custom Middleware
In real applications, middleware is usually created as a separate class.
Step 1: Create Middleware Class
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
Console.WriteLine("Request received");
await _next(context);
Console.WriteLine("Response sent");
}
}
Step 2: Register Middleware
app.UseMiddleware<LoggingMiddleware>();
Now every request will pass through this middleware.
Built-in Middleware in ASP.NET Core
ASP.NET Core provides several built-in middleware components.
Common examples include:
| Middleware | Purpose |
|---|
| UseRouting | Determines the endpoint for a request |
| UseAuthentication | Identifies the user |
| UseAuthorization | Checks access permissions |
| UseStaticFiles | Serves static files |
| UseExceptionHandler | Handles global errors |
Example configuration:
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
Key Takeaways
Middleware forms the HTTP request pipeline in ASP.NET Core.
Each middleware component can process requests and responses.
Use() allows execution before and after the next middleware.
Run() is terminal middleware and stops the pipeline.
Map() creates branch pipelines based on URL paths.
Custom middleware helps implement logging, validation, monitoring, and security.
Conclusion
Middleware is one of the most powerful features in ASP.NET Core. By structuring the request pipeline effectively, developers can handle cross-cutting concerns like authentication, logging, and error handling in a clean and maintainable way.
Understanding middleware and how to create custom components is essential for building scalable and maintainable web APIs.