It is often said that the flow of requests in ASP.NET Core is based on a middleware pipeline for processing requests and responses.
This means that HTTP requests pass through a series of intermediate components (called middlewares) before reaching the controller or action responsible for handling them.
For example, an authentication Middleware can check if the user is authenticated before allowing the request to continue.
Similarly, responses also pass through these intermediate components (in reverse order) before being sent to the client.
It is a very flexible mechanism. Each middleware will be responsible for a specific and independent task regarding the request and response.
Some examples:
- Authentication: Verify the identity of the user.
- Routing: Determine which controller or action will handle the request.
- Error handling: Capture exceptions and generate appropriate responses.
- Response compression: Reduce the size of responses sent to the client.
- Logging: Record information about requests and responses.
Middleware Flow in ASP.NET
Middlewares execute in a sequential order. When a request arrives at the application:
- Entry: The middlewares execute in the order they were registered.
- Exit: The response follows the reverse flow.
Extended Example
To check this, if we run the following code,
app.Use(async (context, next) =>
{
Console.WriteLine("Middleware A: Start.");
await next.Invoke();
Console.WriteLine("Middleware A: End.");
});
app.Use(async (context, next) =>
{
Console.WriteLine("Middleware B: Start.");
await next.Invoke();
Console.WriteLine("Middleware B: End.");
});
app.Run(async context =>
{
Console.WriteLine("Final middleware executed.");
await context.Response.WriteAsync("Hello, world!");
});
It would generate this output in the console:
Middleware A: Start.
Middleware B: Start.
Final middleware executed.
Middleware B: End.
Middleware A: End.
Each Middleware can act on the request or the response as it passes through them. That is, each middleware can,
- Process the request or response (analyze it, use it to perform actions, etc…)
- Modify the request or response.
- Pass the request or response to the next middleware.
- Cancel the request entirely.
Execution Order
Since middlewares execute sequentially, when working with them the order in which we add them to the pipeline is very important.
The order influences how requests and responses are processed, and the behavior can be very different.
For example, the authentication Middleware should be before the authorization Middleware, as we first need to know who the user is before checking their permissions.
Configuring Middleware in ASP.NET Core
In ASP.NET, Middleware is configured in the Program.cs
file (or Startup.cs
in earlier versions). Let’s look at a basic example of how to configure Middleware in an ASP.NET application:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Middleware 1: Exception handling
app.UseExceptionHandler("/Home/Error");
// Middleware 2: Routing
app.UseRouting();
// Middleware 3: Authentication
app.UseAuthentication();
// Middleware 4: Authorization
app.UseAuthorization();
// Middleware 5: Handling static requests (CSS, JS files, etc.)
app.UseStaticFiles();
// Middleware 6: Route definition
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
In this example, we have configured several Middleware components in a specific order:
UseExceptionHandler
: Handles unhandled exceptions and redirects to an error page.UseRouting
: Configures the routing of requests.UseAuthentication
: Verifies the user’s authentication.UseAuthorization
: Checks the user’s permissions.UseStaticFiles
: Serves static files like CSS, JavaScript, and images.MapControllerRoute
: Defines routes for controllers and actions.
Middleware vs Controllers
A common question when starting to work with ASP.NET is whether to use a controller or a middleware for a task.
Short answer, when in doubt you need a controller 😎.
Middlewares are for specific actions in the request pipeline.
Long answer, it depends on the task you are trying to perform:
- Middleware if you need to act on the request before it reaches a controller (for example, general validations, authentication, logging, and error handling)
- Controller if what you need is to handle the business logic of the application, then controllers are the best option (such as interacting with databases or executing specific actions)