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.Request properties
  • 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 RequestDelegate via ApplicationBuilder.Build(), creating a high-performance execution pipeline.
  • Each request receives an HttpContext instance 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() and UseEndpoints(), throwing an InvalidOperationException if 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →