Middleware in ASP.NET Core are software components assembled into an application pipeline to handle every HTTP request and response. They form a chain where each component decides whether to process the request and pass it to the next middleware or short-circuit the pipeline by generating a response directly. Each middleware:
Request delegates are used to build the request pipeline. The request delegates handle each HTTP request. Request delegates are configured using Run, Map, and Use extension methods. An individual request delegate can be specified inline as an anonymous method (called inline middleware) or defined in a reusable class. These inline anonymous methods or reusable classes are called middleware or middleware components. Each middleware in the request pipeline is responsible for invoking the next middleware in the pipeline or short-circuiting the pipeline. When a middleware short-circuits, it's called a terminal middleware because it prevents further middleware from processing the request.
Middleware is typically configured in the “Program.cs” file (or Startup.Configure in older templates) using extension methods on the WebApplication (or IApplicationBuilder) instance
Use(): Chains middleware components together. It can perform actions before and after invoking the next delegate in the pipeline.
Run(): A terminal middleware delegate that stops the pipeline. It does not have a next parameter and is always the last component to run in its branch of the pipeline.
Map(): Used to branch the pipeline based on matching the request path. Requests that match the path are routed to a specific branch, which has its own separate middleware pipeline.
UseWhen(): Conditionally runs a middleware based on a predicate (e.g., checking for a specific query string or header), but the pipeline then rejoins the main sequence.
In .NET Core, middleware components are categorized into two types: terminal and non-terminal.
Terminal middleware is the last component in the middleware pipeline. Its primary role is to generate the response that will be sent back to the client. It can also modify the outgoing response before it’s sent.
Non-terminal middleware refers to any middleware component that isn’t the last one in the pipeline. These middleware components can modify both incoming requests and outgoing responses.
How Middleware Works
The middleware pipeline is bidirectional:
Incoming Request: The request travels through the middleware components in the order they are registered.
Processing: Each component performs its logic and calls await next(context) to pass control to the next component in the pipeline.
Outgoing Response: Once a terminal middleware (like an MVC controller or static file handler) generates a response, the response travels back through the preceding middleware components in reverse order, allowing them to perform post-processing tasks (e.g., logging, adding headers).
![request-delegate-pipeline]()
Each delegate can perform operations before and after the next delegate. Exception-handling delegates should be called early in the pipeline, so they can catch exceptions that occur in later stages of the pipeline.
Built-in Middleware Examples
ASP.NET Core provides a rich set of built-in middleware for common tasks:
Exception Handling: UseExceptionHandler() and UseDeveloperExceptionPage() for managing errors in production and development environments, respectively.
Static Files: UseStaticFiles() for serving static assets like CSS, images, and JavaScript files.
Routing: UseRouting() and endpoint middleware to match requests to application endpoints (e.g., MVC controllers, Razor Pages, Minimal APIs).
Security: UseAuthentication() and UseAuthorization() to verify user identity and permissions.
HTTPS Redirection: UseHttpsRedirection() to redirect HTTP requests to HTTPS.
Custom Middleware
Developers can create custom middleware to handle application-specific cross-cutting concerns, such as API key authentication, request timing, or correlation ID tracking. By creating custom Middleware components, we can add custom logic, modify requests, and process responses to build robust and feature-rich web applications.
In this article we are demonstrate 3 ways to define custom middleware.
1. With Request Delegates
2. By Convention
3. Factory-Based
Let’s go through each of them and implement in the code.
Adding Middleware With Request Delegates
By calling the Use method on the Web Application instance and providing a lambda method with two arguments. The first argument is the HttpContext and the second argument is the actual next request delegate in the pipeline RequestDelegate.
![1]()
By awaiting the next delegate, you are continuing the request pipeline execution. You can short-circuit the pipeline by not invoking the next delegate. This overload of the Use method is the one suggested by Microsoft.
Adding Middleware By Convention
In this approach, we need to create a class that will represent as a middleware. We need to follow below conventions when creating the class so that we can use it as a middleware.
· We need to inject a RequestDelegate in the constructor
· We need to define an InvokeAsync method with an HttpContext argument
· We need to invoke the RequestDelegate and pass it the HttpContext instance
![2]()
After this, we need to call our middleware in pipeline using “UseMiddleware” method:
//Custom Conventional base Middleware
app.UseMiddleware<CustomConventionalMiddleware>();
Adding Factory-Based Middleware
In this approach again we need to create a class but it going implement the IMiddleware Interface. This interface has only one method i.e. InvokeAsync
![3]()
The FactoryMiddleware class will be resolved at runtime from dependency injection and for that we need to register it as a service.
//Register the Middleware in DI Container: The middleware must be registered as a service (typically scoped or transient)
builder.Services.AddTransient<FactoryBasedMiddlewareClass>();
And like previous approach we need to call our middleware in pipeline using “UseMiddleware” method.
//Custom Factory Base Middleware
app.UseMiddleware<FactoryBasedMiddlewareClass>();
As I have implemented all three approach in one solution and declare in program file which look like as below.
![4]()
Based on the sequence of the middleware declaration, the executions of respective middleware’s logic occur.
![5]()
Points to Remember while working with ASP.NET Core Middleware:
The ASP.NET Core Request Processing Pipeline consists of a sequence of middleware components (custom plus built-in) that are going to be called one after the other. If we want to call the next middleware components then we need to use the next method.
Each middleware component in ASP.NET Core Application can perform some operations before and after calling the next component using the next method. A middleware component in ASP.NET Core Application can also decide not to call the next middleware component which is called short- circuiting the request pipeline.
The ASP.NET Core middleware component can access both the incoming request and the outgoing response.
The Run method in ASP.NET Core is the terminating Middleware Component which means it is not possible to call the next middleware component.
The order in which the middleware components are registered in the Configure method defines the order in which these middleware components are going to be invoked on requests and the reverse order for the response. So, the order is critical for defining the security, performance, and functionality of the application.