How ASP.NET Core Processes Incoming HTTP Requests: Pipeline Architecture Explained
ASP.NET Core processes every incoming HTTP request through a composable middleware pipeline that compiles into a single RequestDelegate, which receives an HttpContext and executes registered components sequentially until a response is generated or a 404 is returned.
The dotnet/aspnetcore repository implements a flexible, high-performance request processing architecture that transforms incoming HTTP traffic into executable code. Understanding how ASP.NET Core processes incoming HTTP requests requires examining the middleware pipeline construction, the RequestDelegate compilation, and the role of HttpContext in state management.
The Pipeline Foundation: HttpContext and ApplicationBuilder
Every request begins with two core abstractions defined in the src/Http/Http.Abstractions/src/ directory.
HttpContext: The Request State Container
The HttpContext class, located in [HttpContext.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http.Abstractions/src/HttpContext.cs), encapsulates all HTTP-specific information for a single request. It holds the HttpRequest, HttpResponse, and feature collections that middleware components read and modify throughout the pipeline.
IApplicationBuilder: Pipeline Configuration
The pipeline is configured through IApplicationBuilder, defined in [IApplicationBuilder.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http.Abstractions/src/IApplicationBuilder.cs). The concrete implementation, ApplicationBuilder in [src/Http/Http/src/Builder/ApplicationBuilder.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http/src/Builder/ApplicationBuilder.cs), maintains a list of middleware components registered via Use() and Run() methods.
Building the Request Pipeline
When the application starts, the middleware list compiles into an executable chain.
From Middleware to RequestDelegate
The ApplicationBuilder.Build() method transforms the registered middleware into a single RequestDelegate, defined in [RequestDelegate.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http.Abstractions/src/RequestDelegate.cs). This delegate accepts an HttpContext and returns a Task, representing the entry point for every request.
If no middleware handles the request, Build() supplies a fallback delegate that sets StatusCodes.Status404NotFound and marks the request as unhandled.
Middleware Execution Flow
When a request arrives, the server invokes the compiled RequestDelegate with a newly created HttpContext.
The Middleware Chain
Each middleware component receives the context and a RequestDelegate next parameter representing the remainder of the pipeline. Middleware can:
- Read
HttpContext.Requestproperties - Write to
HttpContext.Response - Store data in
HttpContext.Items - Short-circuit the pipeline by not calling
await next(context) - Pass control forward with
await next(context)
The chain executes in registration order during the request phase, then reverses for the response phase as the stack unwinds.
Endpoint Routing Integration
Modern ASP.NET Core applications typically integrate endpoint routing into the middleware pipeline.
The Routing Middleware
The [EndpointRoutingApplicationBuilderExtensions.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Http/Routing/src/Builder/EndpointRoutingApplicationBuilderExtensions.cs) provides UseRouting() and UseEndpoints() methods that insert routing middleware into the pipeline. When UseRouting() executes, it matches the request to an Endpoint and stores the endpoint's RequestDelegate on the HttpContext.
If the pipeline reaches its end without executing the matched endpoint, ApplicationBuilder.Build() throws an InvalidOperationException with a clear diagnostic message indicating that the endpoint was never invoked.
Practical Implementation Examples
Minimal API Configuration
In Minimal APIs, the WebApplication automatically constructs the pipeline:
var app = WebApplication.CreateBuilder(args).Build();
app.MapGet("/", () => "Hello, world!"); // registers endpoint middleware
app.Run(); // starts the server
Custom Middleware Implementation
Class-based middleware implements IMiddleware from [IMiddleware.cs](https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http.Abstractions/src/IMiddleware.cs):
public class LoggingMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
Console.WriteLine($"{context.Request.Method} {context.Request.Path}");
await next(context); // invoke next middleware
}
}
// Registration
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<LoggingMiddleware>();
var app = builder.Build();
app.UseMiddleware<LoggingMiddleware>(); // adds to the pipeline
app.MapGet("/", () => "OK");
app.Run();
Explicit Pipeline Construction
For advanced scenarios, manually construct the pipeline using ApplicationBuilder:
var app = new ApplicationBuilder(serviceProvider);
app.Use(async (ctx, next) =>
{
// first middleware
ctx.Response.Headers.Add("X-First", "true");
await next(ctx);
});
app.Use(async (ctx, next) =>
{
// second middleware
ctx.Response.Headers.Add("X-Second", "true");
await next(ctx);
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async ctx => await ctx.Response.WriteAsync("Done"));
});
var requestDelegate = app.Build(); // compiles the pipeline
// The server will invoke `requestDelegate(context)` for each request.
Summary
- ASP.NET Core compiles middleware registrations into a single
RequestDelegateviaApplicationBuilder.Build(), creating a high-performance execution pipeline. - Each request receives an
HttpContextinstance that flows through the middleware chain, allowing components to inspect and modify request and response data. - Middleware components call
await next(context)to pass control down the pipeline, enabling both pre-processing and post-processing logic as the call stack unwinds. - Endpoint routing integrates via
UseRouting()andUseEndpoints(), throwing anInvalidOperationExceptionif a matched endpoint is never executed. - Unhandled requests return 404 through a fallback delegate automatically added when
Build()compiles the pipeline.
Frequently Asked Questions
What is the difference between IApplicationBuilder and ApplicationBuilder?
IApplicationBuilder is the abstraction defined in IApplicationBuilder.cs that specifies the contract for registering middleware, while ApplicationBuilder is the concrete implementation in src/Http/Http/src/Builder/ApplicationBuilder.cs that actually stores the middleware list and compiles it into a RequestDelegate via the Build() method.
How does ASP.NET Core handle requests that don't match any endpoints?
When the middleware pipeline completes without a component writing a response or short-circuiting, the compiled RequestDelegate invokes a terminal fallback that sets the response status code to 404 Not Found. This ensures every request receives a deterministic response even when no middleware handles it.
Can middleware modify the response after calling next(context)?
Yes. Because the RequestDelegate chain uses the C# call stack, code executing after await next(context) runs during the response phase as the stack unwinds. Middleware can modify response headers or even replace the response body at this stage, provided the response has not started streaming to the client.
What happens if a middleware throws an exception?
If a middleware throws an exception before calling next(context), the pipeline short-circuits immediately and the exception propagates back up the call stack. The server or exception handling middleware (such as DeveloperExceptionPageMiddleware or ExceptionHandlerMiddleware) catches this and generates an appropriate error response.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →