In this, article, we will learn what is Middleware is and how the Request Pipeline works in ASP.NET Core application.
Table of Contents
Request PipeLine
The Request Pipeline is the mechanism by which requests are processed beginning with a Request and ending with a Response.
The pipeline specifies how the application should respond to the HTTP request. The Request arriving from the browser goes through the pipeline and back
The individual components that make up the pipeline are called Middleware.
What is Middleware
Middleware is a software component that hooks into the request pipeline to handle web requests and generate responses.
Each middleware Process and manipulates the request as it is received from the previous middleware.
It may decide to call the next middleware in the pipeline or send the response back to the previous middleware ( terminating the pipeline )
How it Works
The image below shows us how the Request pipelines combines with the Middleware works in ASP.NET Core application
First, the Http Request arrives (directly or via External web server) at the Application
The Kestrel Web server picks up the Request and creates the httpContext and passes it to the First Middleware in the request pipeline
The First Middleware then takes over, process the request and passes it to the next Middleware. This goes on until it reaches the last middleware
The last middleware returns the request back to the previous middleware, effectively terminating the request pipeline.
Each middleware in the sequence gets a second chance to inspect the request and modify the response on its way back
Finally, the response reaches kestrel, which returns the response back to the client
Any of the middleware in the request pipeline can terminate the request pipeline by simply not passing the request to the next middleware
Configuring the Request Pipeline
To start using any Middleware, we need to add it to the Request pipeline.
This is done in the Configure method of the startup class.
The Configure method gets the instance of IApplicationBuilder, using which we can register our Middleware.
Open the HelloWorld project, which we created in the Getting Started with ASP.NET Core tutorial
Or You can create a new ASP.NET Core Application. In the Project Template select the Empty project template and choose .NET Core and ASP.NET Core 2.0.
Open the Startup class and locate the Configure method and change the code to the following
1 2 3 4 5 6 7 8 9 10 11 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Run(async (context) => { await context.Response.WriteAsync("<div> Hello World from the middleware 1 </div>"); }); } |
We used the app.Run method to register our first middleware, which displays the “Hello world from Middleware 1”, when executed.
The app.Run method gets the instance of the Httpcontext. You can use the Response object from HttpContext to write out some message to HTTP Response
Configure Middleware with Use and Run
The Use and Run method extensions allow us to register the Inline Middlewares to the Request pipeline
The Run method adds the terminating middleware
The Use method adds the middleware, which may call the next middleware in the pipeline
Now let us add one more middleware using the app.Run
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Use(async (context, next) => { await context.Response.WriteAsync("<div> Hello World from the middleware 1 </div>"); }); app.Run(async (context) => { await context.Response.WriteAsync("<div> Hello World from the middleware 2 </div>"); }); } |
The app.Use gets two arguments. One is HttpContext and the second one is a RequestDelegate, which is basically the reference to the next middleware.
Now, run the code again.
The message “Hello world from the middleware 1” appears on the browser. The message from the second middleware does not appear
That is because it is the responsibility of the middleware to invoke the next middleware
We can invoke the next middleware by calling the invoke method of the next middleware, reference to which given to us in the constructor. Modify the code to do that
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Use(async (context, next) => { await context.Response.WriteAsync("<div> Hello World from the middleware 1 </div>"); await next.Invoke(); }); app.Run(async (context) => { await context.Response.WriteAsync("<div> Hello World from the middleware 2 </div>"); }); } |
Now, run the code and you should able to see both the messages
Now, let us add one more middleware and add a message after invoking the next middleware
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Use(async (context, next) => { await context.Response.WriteAsync("<div> Hello World from the middleware 1 </div>"); await next.Invoke(); await context.Response.WriteAsync("<div> Returning from the middleware 1 </div>"); }); app.Use(async (context, next) => { await context.Response.WriteAsync("<div> Hello World from the middleware 2 </div>"); await next.Invoke(); await context.Response.WriteAsync("<div> Returning from the middleware 2 </div>"); }); app.Run(async (context) => { await context.Response.WriteAsync("<div> Hello World from the middleware 3 </div>"); }); } |
You will notice that Middlewares gets a second chance to look at the Request on the way back
Order Matters
Middleware is executed in the same order in which they are added in the pipeline.
Custom Middleware
In the above section, we created inline middlewares using the app.Use and the app.Run
The other way of creating the Middleware is by using classes.
The middleware class is not required to implement any interface or inherit from any class. However, there are two specific rules that you must follow.
1. The middleware class must declare a non-static public constructor with at least one parameter of type RequestDelegate
What actually you get here is the reference to the next middleware in the pipeline. When you invoke this RequestDelegate you are actually invoking the next middleware in the pipeline
2. The middleware class must define a public method named Invoke that takes an HttpContext and returns a Task.This is the method that gets invoked when the request arrives at the middleware
Creating the Custom Middleware
Now, let us a create a simple class to demonstrate this. We name our Middleware as SimpleMiddleware
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class SimpleMiddleware { private readonly RequestDelegate _next; public SimpleMiddleware(RequestDelegate next) { _next = next; } public async System.Threading.Tasks.Task Invoke(HttpContext context) { await context.Response.WriteAsync("<div> Hello from Simple Middleware </div>"); await _next(context); await context.Response.WriteAsync("<div> Bye from Simple Middleware </div>"); } } |
First, in the constructor, we will get the reference to the next middleware in the pipeline. We store it in local variable _next
1 2 3 4 5 6 | public SimpleMiddleware(RequestDelegate next) { _next = next; } |
Next, we must declare an Invoke method, which gets the reference to the HttpContext.
We write out some message to the response and then invoke the next Middleware using await _next(context) Passing it the HttpContext.
1 2 3 4 5 6 7 8 9 10 | public async System.Threading.Tasks.Task Invoke(HttpContext context) { await context.Response.WriteAsync("<div> Hello from Simple Middleware </div>"); await _next(context); await context.Response.WriteAsync("<div> Bye from Simple Middleware </div>"); } |
Next, we need to register the middleware in the request pipeline.
We can do that using the UseMiddleware method of the app as shown below
1 2 3 | app.UseMiddleware<SimpleMiddleware>(); |
Copy the above code in the Configure method
Run the code and you will see the message from our SimpleMiddleware is displayed in the browser.
Extension method
We can create an extension method to simplify the registration of Middleware.
Create another class SomeMiddlewarerExtensions and create a UseSimpleMiddleware as shown below.
1 2 3 4 5 6 7 8 9 | public static class SomeMiddlewarerExtensions { public static IApplicationBuilder UseSimpleMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<SimpleMiddleware>(); } } |
Now instead of
1 2 3 | app.UseMiddleware<SimpleMiddleware>(); |
You can use
1 2 3 | app.UseSimpleMiddleware(); |
to register the middleware.
Summary
The Middleware is software code, which processes the incoming requests. These Middlewares are chained together to form a request pipeline. We create Middleware and register in the configure method of the startup class.
Thanks a lot for the simple and clear explanation.
Thanks this is helpful
Hi, i have tried custom middleware. I am not able to work with it. When i used “await _next(context);” application broke and throw “StatusCode cannot be set because the response has already started.” issue. Kindly revert it.
Hello. Have you find solution?
I found solution myself. You need to add “async” to Invoke method in your middleware and change “return” to “await”