ASP.NET Core Middleware

Today’s header image was created by Christopher Burns, the original source for the image is available here
I’ve mentioned ASP.NET Core’s middleware paradigm in a few earlier posts without actually having described what it is and how it relates to ASP.NET Core applications. This week, I’m going to rectify that by discussing what middleware is and how it relates to the way that classic ASP.NET applications handle HTTP Requests and Responses.
“Classic ASP.NET” here refers to the full .NET Framework implementation of ASP.NET (i.e. not ASP.NET Core)
What Classic ASP.NET Does
In ASP.NET HTTP Requests are handled by HTTP Modules, these HTTP Modules are used to accept a request and generate a response. ASP.NET includes things called HTTP Handlers, but these are explicitly used to handle requests for specific files of file extensions.
we’ll see how HTTP Handlers are implemented in ASP.NET Core in a moment
Here is Microsoft’s description of an HTTP Module:
An HTTP module is an assembly that is called on every request that is made to your application. HTTP modules are called as part of the request pipeline and have access to life-cycle events throughout the request. HTTP modules therefore let you examine incoming requests and take action based on the request. They also let you examine the outgoing response and modify it.
HTTP Modules are loaded (by IIS) into the ASP.NET Application Life Cycle
which you can read more about here
and, depending on which version of IIS you are using, they are either loaded separately in to the Web Server request pipeline (in the case of IIS 6.0) or can be integrated with the Web Server pipeline (in the case of IIS 7.0).
The Pipeline
The request pipeline is constructed by placing each of the HTTP Modules in a serial list. The incoming response is fed into each HTTP Module in turn, until one of the modules creates a response.
Let’s say that we have loaded HTTP modules for logging, user auth, and database access.
maybe we’re allowing authenticated users to request stuff from an API
It might end up looking something like this:

We have a request coming in, and it is being fed into each of the HTTP modules which make up the pipeline, in turn. The request can take a path all of the way through the pipeline to the database, at which point our pipeline will generate a response.
I’ve simplified this diagram, as it’s an example
side note
Before we can into the pipeline any further I just wanted to point out that, for the purposes of example, each of the modules we’ll see in these diagrams can be thought of as black boxes. As such, we’re not concerned with how each of them acts on the response, just that they do.
back on track
If any of these modules generated a response of it’s own, that would be sent back to the user. An example of this would be if the user was not authenticated, in that instance we would not want the request to hit the database, so the user auth module generates a response saying that the user is not authenticated (maybe as little as generating a 401 error).
The above example might like look like the following:

At this point, the response is generated. However to generate the response, we take the same path as the request. This means that our response first goes through Logging, into User Auth, then (if validated) in to Database.

The response takes the same trip as the request, but is generated at the same stage that the request stopped. This response is then sent to the client.
In the above example:
- A request comes in and is passed to Logging
- The request is passed to User Auth, which fails
- A response is created
- The Response starts at the beginning of the pipeline and is passed into Logging
- It is then passed to User Auth
- From User Auth it is sent back to the client
ASP.NET Core And What It Does
ASP.NET Core has an application builder, which we’ve seen before. Here is an example:
// This method gets called by the runtime. Use this method to | |
// configure the HTTP request pipeline. | |
public void Configure(IApplicationBuilder app, IHostingEnvironment env, | |
ILoggerFactory loggerFactory) | |
{ | |
loggerFactory.AddConsole(); | |
if (env.IsDevelopment()) | |
{ | |
app.UseDeveloperExceptionPage(); | |
} | |
app.UseMvc(routes => { | |
routes.MapRoute( | |
name: "default", | |
template: "{controller=Home}/{action=Index}/{id?}"); | |
}); | |
app.UseWelcomePage(); | |
} |
Everything that we add to the IApplicationBuilder object here is added to the request pipeline. Each item added to the pipeline is called middleware. In the above example we’re setting up a pipeline which should attempt to handle our requests with the MVC middleware, then fall back on the WelcomePage one.
the WelcomePage middleware generates and returns a friendly HTML page
Microsoft describe middleware describe as:
Middleware is software that is assembled into an application pipeline to handle requests and responses. Each component chooses whether to pass the request on to the next component in the pipeline, and can perform certain actions before and after the next component is invoked in the pipeline. Request delegates are used to build the request pipeline. The request delegates handle each HTTP request.
source: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware#what-is-middleware
Responses take the same trip as the request for which they are created
just like classic ASP.NET
but in reverse.
ah
If it helps, try to think of the ASP.NET Core request and response pipeline as an instance of a first in, last out stack.
In the above example we add push each item onto the stack:
- Developer Exception Page
- MVC Routing
- Welcome Page
As a request comes in, it works its way up the stack until it reaches a middleware which produces a response, at which point the response traverses back down the middleware stack.
I guess a ladder would be a better analogy: You climb the ladder until you reach the level you need, then climb back down.
Each piece of middleware in .NET Core is responsible for either passing the request to the next piece of middleware in the pipeline, or generating a response.
The above code example produces a middleware pipeline which looks like the following:
Let’s say we make a request andthat our request matches a controller action.
as an example, let’s say that it’s for /Home/About
Our request would get to the MVC Routing middleware and a matching controller and action would be picked. The matching controller and action are then called and the response that the chosen action generates will be passed back down the pipeline in the opposite direction.
i.e. from MVC Routing to Developer Exception page and out to the client
The entire process would look something like this:

Middleware Order
As should be obvious here, the ordering of middleware is crucial. If we changed the order in which we added the middleware components to the IApplicationBuilder object to match the following:
// This method gets called by the runtime. Use this method to | |
// configure the HTTP request pipeline. | |
public void Configure(IApplicationBuilder app, IHostingEnvironment env, | |
ILoggerFactory loggerFactory) | |
{ | |
loggerFactory.AddConsole(); | |
if (env.IsDevelopment()) | |
{ | |
app.UseDeveloperExceptionPage(); | |
} | |
app.UseWelcomePage(); | |
app.UseMvc(routes => { | |
routes.MapRoute( | |
name: "default", | |
template: "{controller=Home}/{action=Index}/{id?}"); | |
}); | |
} |
And put in a request for a controller action,
perhaps /Home/About again
we’d get a welcome page. But why?
Because of the WelcomePage middleware.
We requested a resource and the WelcomePage middleware did it’s job (the job of the WelcomePage middlware is to generate a welcome page and return it). This short circuits the rest of the pipeline, exactly the same way that the MVC Routing middleware did earlier.
All of this is the behaviour of the WelcomePage middleware and something which could cause some headaches for developers who are unaware of this.
Take a look through the WelcomePage middleware class for more information on how it works.
As as side note, the welcome page looks like this:
Much more cheery than a 404 error screen, right?
you wouldn’t enable this in production, I’m just showing it here because it’s fun
Can I Build My Own?
Of course. I’ll go into it a little deeper next time (as we’ll build one in the next blog post), but it’s as simple as creating a class, taking a RequestDelegate as a constructor parameter, and having a method which returns a Task called Invoke which takes an HttpContext object.
simple
But we’ll leave that for next time.
gotta keep you coming back for more somehow, right?
Conclusion
We’ve seen how ASP.NET Core builds up it’s request pipeline and how that is subtly different to how Classic ASP.NET builds up it’s pipeline. We’ve seen how we need to be very aware of the order in which we add middleware to the pipeline, as we could get errors with requests if an early middleware generates a response for us before we expect it to.