Middleware got introduced in ASP.NET with .NET Core. ASP.NET Core Middleware allows controlling the web application request pipeline. The middleware approach got introduced to configure the ASP.NET request pipeline.
Table of Contents
Introduction
ASP.NET Core Middleware is a piece of code that gets executed on every request. ASP.NET Core comes with built-in middleware e.g. routing, session, MVC, etc and one can write their own custom middleware as well. Middleware forms an application pipeline to handle requests & responses and a pipeline consists of a series of middleware that gets executed in the configured order.
Request pipeline can be a combination of built-in Middleware, third party middleware, or your own custom middleware like logging middleware, exception middleware, etc.
Middleware can be directly added using request delegates that handle each HTTP request. Request delegates can be configured using Run, Use & Map methods directly in the startup.cs file in Configure Method. Alternatively, middleware can also be configured using a separate reusable class.
Based on the request type, each Middleware can decide whether to perform work on the request (before & after the next component in a pipeline), do nothing, and forward the request to the next middleware or short circuit the pipeline by generating a response. When middleware short circuits pipeline it’s called terminal middleware.
Implement ASP.NET Core Middleware
ASP.NET Core Middleware can be implemented using request delegates – Run, Use, Map in the startup.cs file and also by implementing custom middleware class. Both methods have been covered below.
Create a middleware with IApplicationBuilder – Run, Use and Map
Configure the HTTP pipeline using Run, Use, and Map request delegates. Request delegates can be registered as in-line anonymous methods through a startup.cs in Configure Method. ASP.NET Core Request Delegates – Run, Use & Map can be used to alter the request pipeline.
Run – Generates a response and terminates the pipeline i.e. no other middleware will run after this. Typically should be placed at the end of the pipeline. When a request is not passed to the next delegate its called short-circuiting the request pipeline (i.e. terminal middleware)
public class Startup { public void Configure(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync(“Hello, Some Response Text!!”); }); } }Use – Connects multiple middlewares in a pipeline. Performs tasks before and after the next delegate. Parameter next will take to the next middleware in the pipeline. Conditionally you can even short-circuit the pipeline by not calling the next parameter.
public class Startup { public void Configure(IApplicationBuilder app) { app.Use(async (context, next) => { // Code Logic that does not short cicuits pipeline await next.Invoke(); // Code Logic }); app.Run(async context => { await context.Response.WriteAsync("Some Response From next delegate in pipeline!!"); }); } }
Don’t call next.Invoke() after the response has been sent to the client as any changes to the response after the response has started will result in an exception.
Map – Maps are used to reroute the request to different middleware components based on incoming requests. Map and MapWhen reroute the request pipeline based on matches of the specified request path.
public class Startup { private static void HandleContact(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Contact us at reach.rsmrocks@gmail.com"); }); } private static void HandleAbout(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hi from rsmrocks"); }); } private static void HandleWelcome(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Welcome to our Blog"); }); } public void Configure(IApplicationBuilder app) { app.Map("/contact", HandleContact); app.Map("/about", HandleAbout); app.MapWhen(context => context.Request.Query.ContainsKey("welcome"), HandleWelcome); app.Run(async context => { await context.Response.WriteAsync("Hello from delegate without Map!"); }); } }
Create a middleware using custom middleware
Request Delegates inside a startup.cs will be difficult to maintain when request pipeline complexity increases. We generally want to keep code clean and also allow middleware to handle single responsibility so middleware is implemented as standalone reusable classes. Middleware class is a class with an invoke method that contains code logic and also has a constructor that accepts a RequestDelegate.
public class MyFirstMiddleware { private readonly RequestDelegate _next; public MyFirstMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { // Code Logic before next middleware await _next(httpContext); // Code Logic after next middleware } }
We can directly use the above middleware class in a startup.cs but to keep it clean we can add the below extension class which uses IApplicationBuilder and then use this extension in configure method in a startup.cs
public static class MyFirstMiddlewareExtensions { public static IApplicationBuilder UseMyFirstMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware(); } }
Now our configure method in the startup class will look like this
public void Configure(IApplicationBuilder app) { app.UseMyFirstMiddleware(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
Middleware Order
ASP.NET Core request processing pipeline consists of a series of middleware that handles the request/response one by one i.e. they are called one by one by the .NET core framework. The sequence in which these middlewares are configured is called Middleware Order.
The order in which ASP.NET Core Middleware Components are added in the Configure method of the Startup class method defines the order in which middleware components are invoked for handling requests as well as responses.
Summary
- ASP.NET Core Middleware is a piece of code that gets executed on every request
- Request pipeline can be a combination of built-in Middleware, third party middleware or your own custom middleware like logging middleware, exception middleware, etc.
- Middleware can be directly added using request delegates like Run, Use & Map in the startup.cs file in Configure Method. Alternatively, middleware can also be configured using a separate reusable class.
You can also check my another Article explaining how to analyze ASP.NET application issues – https://procodeguide.com/programming/analyze-aspnet-application-issues/
References: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.0#use-run-and-map
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.2