Terminology
- App Pipeline:
- When an http request comes from the browser it is processed through a chain of function calls. Each function performs some work and calls the next one till an http response is sent to the client. The chain of function calls is called the pipeline.
- Middleware:
- Each piece of code/function in the app pipeline is called middleware.
- Short-circuiting
- When a function doesn't call the next one. For example, a static file middleware processes a request for a static file, and short-circuits the pipeline.
- Terminal Middleware:
- The middleware that doesn't invoke any successor is called the terminal middleware. Most commonly it is the last component. But any component that short-circuits the request pipeline is a terminal middleware. Short circuiting can, for example, be done by an authorization component.
- RequestDelegate delegate:
- A function that accepts an HttpContext as one of its parameters and returns a Task. Request delegates handle each http request. In the example below the Run [extension method] calls an anonymous inline RequestDelegate.
public void Configure(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hello, World!"); }); }
Simplest App - Just one Terminal Middleware
Run is an extension method on IApplicationBuilder that adds a terminal middleware delegate to the pipeline.
Following is the simplest aspnet core app with just one terminal middleware that writes Hello, World to any request that comes in.
public class Startup { public void Configure(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hello, World!"); }); } }
App with two Middleware
"Use" is an extension method that can chain request delegates together.
Use method receives a "next" parameter for chaining.
public class Startup { public void Configure(IApplicationBuilder app, ILogger<Startup> logger) { StringBuilder sbResponse = new StringBuilder(); // first component app.Use(async (context, next) => { // Do work before passing to next sbResponse.Append("Hi . . ."); // call the next delegate in the pipeline await next(); // some work after logger.LogInformation("my log info"); }); // terminal app.Run(async context => { sbResponse.Append("Hello, World!"); await context.Response.WriteAsync(sbResponse.ToString()); }); } }
Branching a Middleware with Map
Map extensions are used to branch the request pipeline based on matches of the request path.
public class Startup { public void Configure(IApplicationBuilder app) { app.Map("/map1", (app) => { app.Run(async context => { await context.Response.WriteAsync("map 1"); }); }) ; app.Map("/map2", (app) => { app.Run(async context => { await context.Response.WriteAsync("map 2"); }); }); app.Run(async context => { await context.Response.WriteAsync("non-map"); }); } }
Branching with UseWhen and MapWhen
They branch the request pipeline based on the results of a predicate.
IMPORTANT: UseWhen doesn't stop executing the rest of the pipeline when the delegate returns true. So "next()" is implied.
public void Configure(IApplicationBuilder app) { StringBuilder sb = new StringBuilder(); // predicate based use app.UseWhen(context => context.Request.IsHttps, app => { sb.Append("Inside UseWhen . . ."); // no need to call next // automatically calls next }); // predicate based branching app.MapWhen(context => context.Request.IsHttps, app => { app.Run(async context => { sb.Append("and Map When!"); await context.Response.WriteAsync(sb.ToString()); }); }); }
Middleware Components provided by the ASP.NET Core library
Following are some of the middleware components:
- UseDeveloperExceptionPage()
- UseHsts()
(HTTP Strict Transport Security Protocol) - UseHttpsRedirection()
- UseStaticFiles()
returns static files and short-circuits further request processing. - UseRouting()
to route requests - UseAuthentication()
- UseSession()
- UseEndPoints()
(terminal for matching routes)
Typical Middleware
Following is the recommended order. The order is critical for security, performance, and functionality.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); // app.UseCookiePolicy(); app.UseRouting(); // app.UseRequestLocalization(); // Cross-Origin Resource Sharing // app.UseCors(); app.UseAuthentication(); app.UseAuthorization(); // app.UseSession(); // app.UseResponseCaching(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
Notes on UseEndpoints
- Must be used with UseRouting()
- used to add endpoints for requests based on http request type [Post, Get, Put, etc.,] and on the matched route.
- used to add endpoints for razor pages.
- used to add endpoints for matched controller routes.
- and so on
Following is an example:
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); // post requests endpoints.MapPost("/", async context => { await context.Response.WriteAsync("Hello World!"); }); endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"); }); } }
Exercises on Middleware
for solutions please join our Course on ASP.NET Core with Project ("Find a Doctor")- Without using razor pages, write a simple ASP.NET Core application that writes a "Hello World!" message to the response stream when an HTTP GET request is made to the home page of your website.
- Without using razor pages, write a simple ASP.NET Core application that sends a FORM consisting of an input textbox and a submit when an HTTP GET request is made to the home page of your website. The application should handle the posted data and respond by sending back the upper case equivalent of the input typed by the user.
Comments and Discussion
Please write your comments and discussion on the following youtube video itself:
This Blog Post/Article "Middleware in ASPNET Core" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.