ASP.NET Core Request Pipeline
The ASP.NET Core request pipeline is the fundamental mechanism by which incoming HTTP requests are processed and outgoing HTTP responses are generated. It's a series of components, known as middleware, that are executed sequentially for each request.
Understanding the Pipeline
When a request arrives at your ASP.NET Core application, it enters the pipeline at the beginning and passes through each middleware component. Each middleware has the opportunity to:
- Execute code before the request proceeds to the next middleware.
- Execute code after the request has been processed by subsequent middleware (i.e., on the way back out of the pipeline).
- Short-circuit the pipeline, meaning it can generate a response and prevent further middleware from being executed.
Key Components and Concepts
Middleware
Middleware is the building block of the ASP.NET Core pipeline. Each piece of middleware is responsible for a specific task, such as:
- Handling static files
- Authentication
- Authorization
- Routing
- Logging
- Error handling
- CORS (Cross-Origin Resource Sharing)
Middleware components are arranged in a specific order within the pipeline. This order is critical, as it determines the sequence of operations. For example, authentication middleware typically runs before authorization middleware.
Use...() Extension Methods
You configure the ASP.NET Core pipeline in your application's Startup.cs (or Program.cs in .NET 6+) file using extension methods on the IApplicationBuilder interface. These methods, like app.UseStaticFiles(), app.UseRouting(), and app.UseAuthentication(), add specific middleware to the pipeline.
// Example in Startup.cs (older .NET Core versions)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
}
// Example in Program.cs (.NET 6 and later)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.Run();
Order Matters
The order in which you add middleware is crucial. Consider the following example:
app.UseRouting() after middleware that might short-circuit the request (like app.Run()), routing will never be reached. Similarly, app.UseAuthentication() and app.UseAuthorization() must come after app.UseRouting() so that the router can determine which endpoints need authentication/authorization.
Terminal Middleware
Some middleware, often added last, can terminate the pipeline. For instance, app.Run() is a simple middleware that takes a delegate and executes it, but it doesn't call the next middleware in the pipeline.
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from the terminal middleware!");
});
Common Middleware Components
- Static Files Middleware: Serves static files like HTML, CSS, JavaScript, and images.
- Routing Middleware: Selects the appropriate endpoint (e.g., MVC controller action, Razor Page) for the incoming request based on its URL.
- Authentication Middleware: Authenticates the user.
- Authorization Middleware: Authorizes the authenticated user to access the requested resource.
- Endpoint Routing Middleware: Executes the selected endpoint.
- Websockets Middleware: Handles WebSocket connections.
- Exception Handling Middleware: Catches exceptions and generates an appropriate error response.
- Directory Browsing Middleware: Enables browsing of directories containing static files.
Custom Middleware
You can create your own custom middleware to add specific functionality to your application. A middleware component is typically a class with a constructor that accepts RequestDelegate and an InvokeAsync method that processes the HttpContext.
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Code to execute before the next middleware
Console.WriteLine($"Request Path: {context.Request.Path}");
await _next(context); // Call the next middleware in the pipeline
// Code to execute after the next middleware (response processing)
Console.WriteLine($"Response Status Code: {context.Response.StatusCode}");
}
}
// In Startup.cs (or Program.cs):
// app.UseMiddleware<CustomMiddleware>();
Use() or UseWhen() extension methods directly in your pipeline configuration without creating a separate class.
Benefits of the Pipeline Architecture
- Modularity: Each middleware handles a specific concern, making the codebase cleaner and easier to manage.
- Composability: Middleware components can be easily added, removed, or reordered to customize the request processing.
- Flexibility: Allows for fine-grained control over how requests are handled.
- Reusability: Many middleware components are generic and can be used across different ASP.NET Core applications.
This document provides a high-level overview of the ASP.NET Core request pipeline. For detailed information on specific middleware or advanced customization, please explore the related topics in the navigation pane.