MSDN Documentation

Microsoft Developer Network

Understanding Middleware Order

The order in which middleware components are configured in the ASP.NET Core request pipeline is crucial. Each middleware has the opportunity to act on an incoming HTTP request before it reaches the next middleware, and to act on the outgoing HTTP response after the downstream middleware has finished processing.

The Request Pipeline

Imagine the middleware pipeline as a series of steps that a request must pass through. When a request arrives at your ASP.NET Core application:

  • It enters the pipeline at the first configured middleware.
  • If the middleware handles the request, it might short-circuit the pipeline, preventing subsequent middleware from executing.
  • If the middleware does not handle the request, it passes the request to the next middleware in the sequence.

This continues until the request reaches the final middleware, which typically sends the response back to the client. The response then travels back up the pipeline, with each middleware having a chance to modify it.

Use(), Map(), and MapWhen()

In your application's Startup.cs (or Program.cs in newer .NET versions), you configure the middleware pipeline using methods on the IApplicationBuilder interface:

  • Use(Func<RequestDelegate, RequestDelegate> middleware): This is the most fundamental method. It takes a delegate that wraps the existing RequestDelegate (the next middleware in the pipeline) and returns a new RequestDelegate. This allows you to insert your custom middleware or modify the pipeline's behavior.
  • Map(PathString pathMatch, Action<IApplicationBuilder> applicationBuilder): This method branches the pipeline. If the request path starts with the specified pathMatch, the middleware configured within the provided applicationBuilder will execute. This is useful for segmenting your application logic.
  • MapWhen(Func<HttpContext, bool> predicate, Action<IApplicationBuilder> applicationBuilder): Similar to Map, but allows branching based on a custom condition (a predicate) evaluated against the HttpContext.

Example of Middleware Order

Consider the following configuration in Startup.cs:


public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // Middleware executed for every request
    app.Use(async (context, next) =>
    {
        context.Response.Headers.Add("X-Request-Start", DateTimeOffset.UtcNow.ToString());
        await next(context); // Pass control to the next middleware
    });

    // Static files middleware should usually be early
    app.UseStaticFiles();

    // Routing middleware
    app.UseRouting();

    // Authentication and Authorization middleware
    app.UseAuthentication();
    app.UseAuthorization();

    // Endpoint mapping
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
        endpoints.MapRazorPages();
    });

    // Middleware executed only if no endpoint was matched
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello from the final middleware!");
    });
}
                

Key Considerations for Order:

  • Static Files: Configure UseStaticFiles() early to serve static content directly without going through the full pipeline.
  • Routing: UseRouting() should typically be placed before authentication and authorization, as it enables the routing logic that determines which endpoints are available.
  • Authentication/Authorization: These must be placed after UseRouting() so they can inspect the route data and determine what resources are being requested.
  • Endpoints: UseEndpoints() is where your application's actual logic (MVC controllers, Razor Pages, minimal APIs) is wired up.
  • Exception Handling: Middleware for handling exceptions (like UseExceptionHandler()) should generally be placed at the very beginning of the pipeline to catch errors from any subsequent middleware.
  • Final Middleware: A middleware added using Run() (like the example above) will execute only if no other middleware in the pipeline has handled the request and terminated it. It should typically be the last middleware added.
Important: Misordering middleware can lead to unexpected behavior, such as authentication checks being bypassed or static files not being served correctly. Always consider the flow of the request and response when defining your pipeline.

By understanding the order and the role of each middleware, you can effectively build a robust and efficient ASP.NET Core application.