How can you branch the request pipeline using `MapWhen` ?
Question
How can you branch the request pipeline using `MapWhen` ?
Brief Answer
MapWhen conditionally branches the ASP.NET Core request pipeline based on a dynamic predicate. It’s a powerful tool for applying specific middleware only when certain conditions are met.
How it Works
You provide a Func<HttpContext, bool> predicate. If this predicate evaluates to true, a distinct, isolated middleware pipeline (the “branch”) is executed. If false, the request completely bypasses the branch and continues down the main pipeline.
Key Distinction from Map
Mapbranches solely based on a path prefix.MapWhenbranches based on any condition derived from theHttpContext(path, headers, query strings, user identity, etc.), offering much greater flexibility.
Practical Use Cases (Why Use It?)
- Applying authentication or authorization middleware only to specific API endpoints.
- Implementing feature flags based on request headers or user claims.
- Serving environment-specific logic or content conditionally.
Benefits to Convey
- Granular Control: Precisely define when specific middleware applies.
- Organization & Maintainability: Helps separate concerns and keeps your middleware pipeline cleaner and easier to manage/test.
Consideration
While the predicate evaluation introduces a minor performance overhead, it’s typically negligible for most applications, and the benefits of conditional execution usually outweigh this.
Super Brief Answer
MapWhen conditionally branches the ASP.NET Core request pipeline using a Func<HttpContext, bool> predicate. If the predicate is true, a distinct middleware branch executes; otherwise, the main pipeline continues.
This offers highly flexible conditional middleware execution (unlike Map‘s path-only branching), ideal for scenarios like applying authentication to specific API paths, implementing feature flags, or organizing complex pipelines.
Detailed Answer
In ASP.NET Core, the request pipeline is a series of middleware components that process incoming HTTP requests and outgoing responses. Understanding how to control and customize this flow is crucial for building robust and efficient applications. One powerful technique for conditional request handling is branching the pipeline using MapWhen.
Direct Summary
MapWhen creates a separate branch in the middleware pipeline based on a predicate (condition). If this predicate returns true, the branch’s middleware executes; otherwise, the request continues down the main pipeline. Think of it as an “if” statement for your middleware configuration.
Understanding MapWhen in ASP.NET Core
MapWhen is an extension method for IApplicationBuilder that allows you to conditionally create a new, distinct middleware pipeline based on the characteristics of an incoming HTTP request. This provides a flexible way to apply specific middleware components only when certain conditions are met.
Key Concepts of MapWhen
The Predicate: Your Branching Condition
The core of MapWhen is its predicate. This is a Func<HttpContext, bool> that evaluates the current HttpContext and returns true or false. If true, the request enters the branched pipeline; if false, it bypasses the branch and continues down the main pipeline.
The predicate operates on the HttpContext, allowing you to make branching decisions based on any detail of the request: headers, path, query string, user identity, cookies, and more. This flexibility enables highly specific and dynamic middleware application.
Branching Logic: Separate Pipeline Execution
When MapWhen‘s predicate evaluates to true, a distinct, isolated middleware pipeline is created and executed for that request. This means that middleware configured within this branch will process the request, and crucially, the request will not return to the main pipeline after the branch’s execution, unless explicitly redirected or re-routed.
If the predicate evaluates to false, the request completely bypasses the branch and continues processing middleware in the main pipeline as if the MapWhen statement didn’t exist. This ensures that the main pipeline remains unaffected by the conditional logic.
Practical Use Cases for MapWhen
MapWhen excels in scenarios requiring conditional middleware execution. Here are common applications:
- API Versioning or Environment-Specific Logic: Apply specific middleware (e.g., authentication schemes, logging levels, or custom headers) based on a request’s path prefix (e.g.,
/api/v1vs./api/v2), a custom header, or an environment variable. - Authentication/Authorization for Specific Paths: Protect certain routes or API endpoints with authentication or authorization middleware, while leaving other parts of your application public.
- Serving Static Files Conditionally: Serve static files from a specific directory only if the request path matches certain criteria, bypassing other middleware for performance.
- Feature Flags: Enable or disable certain features or middleware pipelines based on a feature flag present in a request header, query string, or user claim.
- Redirecting Old URLs: Implement conditional redirects for legacy URLs to new paths.
Distinguishing MapWhen from Map
While both Map and MapWhen are used for branching the middleware pipeline, they differ fundamentally in their branching criteria:
-
Map: Branches solely based on a path prefix. If the request path starts with the specified prefix, it enters the branch. For example,app.Map("/api", ...)would branch all requests starting with/api. -
MapWhen: Branches based on the result of a predicate function, offering much greater flexibility beyond simple path matching. You can combine path matching with other conditions (e.g.,/apiAND user role is admin).
In essence, Map is a specialized form of branching for path-based routing, whereas MapWhen provides general-purpose conditional branching for any request characteristic.
Real-World Insights and Best Practices
Granular Control with Predicates
The predicate function in MapWhen provides fine-grained control over when a branch is executed. This is incredibly powerful for implementing dynamic application behavior.
“In a previous project, we used MapWhen to implement feature flags. We had a header called ‘X-Feature-Beta’. Our predicate checked if this header was present and if the current user was part of the beta testing group. Only then would the request enter the branch containing the beta feature middleware. This allowed us to deploy new features to production but activate them only for a select group, giving us granular control over feature rollouts without separate deployments.”
Organizing Middleware for Clarity
MapWhen helps organize your middleware by separating concerns based on request characteristics, leading to cleaner and more manageable code.
“Think of MapWhen like a traffic management system. Just as different roads lead to different destinations, MapWhen routes requests to different middleware pipelines based on their characteristics. This keeps your middleware organized and focused. For example, all API-related middleware can reside in a branch triggered by a predicate checking for the ‘/api’ path prefix, separating it neatly from the main application’s middleware, improving overall readability and structure.”
Enhancing Maintainability and Testability
Using MapWhen enhances maintainability and testability by isolating specific logic within branches, making it easier to test and debug individual components.
“Using MapWhen dramatically improved the maintainability and testability of our application. By isolating specific functionality within branches, we could test each branch independently. For instance, we had a branch dedicated to handling file uploads. This isolation made it much easier to write unit tests specifically for the file upload logic without involving the rest of the application’s pipeline. Debugging was also simplified as we could focus on the relevant branch when issues arose, reducing the overall complexity of troubleshooting.”
Considering Performance
While evaluating the predicate in MapWhen introduces a minor overhead, the benefits of conditional execution often outweigh this cost. However, in highly performance-sensitive scenarios, it’s worth considering optimizations.
“While the predicate evaluation in MapWhen introduces a small performance overhead, it’s usually negligible for most applications. However, in highly performance-sensitive scenarios, it’s worth considering. In one project, we initially used MapWhen to apply logging middleware based on a complex predicate. We found that this predicate was being evaluated for every single request, even though the logging was only required for a small fraction. We optimized this by caching the predicate result for frequent request patterns or by simplifying the predicate, significantly reducing the overhead while still enjoying the benefits of conditional execution.”
Code Sample
The following example demonstrates how to use MapWhen to branch the pipeline for requests starting with /api:
// Configure middleware pipeline within the Configure method of Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... other middleware that applies to ALL requests ...
// Branch the pipeline if the request path starts with /api
app.MapWhen(context => context.Request.Path.StartsWithSegments("/api"), appBuilder =>
{
// Middleware specific to /api requests
// For example, add authentication or authorization for API calls
appBuilder.UseAuthentication();
appBuilder.UseAuthorization();
// This middleware will only run for requests that entered this branch
appBuilder.Run(async context =>
{
// Handle /api requests
await context.Response.WriteAsync("Handling API request in a branched pipeline.");
});
});
// ... other middleware that applies to ALL requests,
// but only if they DID NOT enter the MapWhen branch ...
// Default middleware for requests that don't match the predicate
// This will handle requests like "/" or "/home"
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from the main pipeline!");
});
}
In this example, requests to /api/... will go through the authentication and authorization middleware within the MapWhen branch, while other requests will bypass it and be handled by the main pipeline’s final app.Run.

