In previous article, we have discussed about Middleware components in ASP.NET Core. In this article, we will discuss how to configure them using Run and Use extension methods. Please make sure you read article about Middleware first.
Before we begin to know the “HOW” part, it would be great to understand the concept of why branching the application request pipeline might be required.
As you know, the Configure method from Startup class, defines the sequence of request delegates through which request would pass. In real world, there may be cases where you want different middleware components for subset of application requests.
You may want to serve the static files (scripts or images) without even checking authorization of the user, thereby reducing load on the server. While the admin module from the web application needs more checks (e.g. checking if application is being requested from internal users).
Now, let’s see how to branch the request processing pipeline.
Branching
The Map extension methods are used to branch the request processing pipeline. The Map extension method takes input path (complete or partial) and if the incoming request’s path matches, the new branch (second parameter of Map method) is executed.
Below is sample code showing how branching works.
public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.Map("/first", handleFirstBranch); app.Map("/second", handleSecondBranch); app.Run(async context => { await context.Response.WriteAsync("The default pipeline is executed"); }); } public void handleFirstBranch(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("First branch is executed"); }); } public void handleSecondBranch(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Second branch is executed"); }); } }
Below points show how the request would be processed and how the pipeline branches.
- https://localhost/ or https://localhost/token would print The default pipeline is executed response.
- https://localhost/first or https://localhost/first/token would print the First branch is executed
- https://localhost/second or https://localhost/second/token would print the Second branch is executed
Nested Branches
The request pipeline can also have nested branches. In above snippet, the Map method can be used to further branch the https://localhost/first branch.
When Map
is used, the matched path segments are removed from HttpRequest.Path
and appended to HttpRequest.PathBase
for each request.
The Map method can either take a single token from URL (like /first) or it can take multiple tokens together (like /first/level-2). If multiple tokens are specified together, then the request pipeline branches only if all of the specified tokens match.
Below code shows how the code snippet above can be further modified to form nested branches.
// Delegate implementation for app.Map("/first", handleFirstBranch) public void handleFirstBranch(IApplicationBuilder app) { // HttpRequest.BasePath = https://localhost/first // Further nesting: branch if URL starts with https://localhost/first/level-2 app.Map("/level-2", app => { // Further nesting: branch if URL starts with https://localhost/first/level-2/level-3 app.Map("/level-3", app => { app.Run(async context => { await context.Response.WriteAsync("Branch: /first/level-2/level-3"); }); }); app.Run(async context => { await context.Response.WriteAsync("Branch: /first/level-2"); }); }); // Multiple tokens together from HttpRequest.Path // Branch if url starts with https://localhost/first/step-2/step-3 app.Map("/step-2/step-3", app => { app.Run(async context => { await context.Response.WriteAsync("Branch: /first/step-2/step-3"); }); }); app.Run(async context => { await context.Response.WriteAsync("Branch: /first"); }); }
Conditional Branches
There might be real world scenarios when the application needs to branch the request processing pipeline based on certain conditions.
The MapWhen delegate can be used in such cases. It branches the request pipeline only if the predicate returns true. Below code snippet checks if there is a query string variable with name token
is present. If it is available, then the pipeline branches.
public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.MapWhen(context => context.Request.Query.ContainsKey("token"), handleTokenBranch); app.Run(async context => { await context.Response.WriteAsync("The default pipeline"); }); } public void handleTokenBranch(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Token branch"); }); } }
I hope you find this information helpful.
Andriy Kravets is writer and experience .NET developer and like .NET for regular development. He likes to build cross-platform libraries/software with .NET.