ASP.NET Core Middleware

Jamie Taylor8 comments
ASP.NET Core Middleware Header ImageSource: https://stocksnap.io/photo/TNTMA655QV Copyright 2017: Christopher Burns

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.

source: https://msdn.microsoft.com/library/bb398986.aspx

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:

Classic ASP.NET Request pipeline example
Talk about mad drawing skills

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:

Classic ASP.NET pipeline auth fail
Auth here has failed, so we’ll generate a response

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.

Classic ASPI.NET Response Generated
The directions of the arrows here are intentional. At the User Auth stage, we’re taken back to the beginning of the pipeline and have to traverse it again

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:

ASP.NET Core Middleware pipleline

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:

ASP.NET Core Middleware pipleline Controller matches
The direction of the arrows are equally as intentional here, too.
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.

ASP.NET Core Middleware pipleline WelcomePage

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:

ASP.NET Core default Welcome Screen

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.

If this post has helped you out, please consider   Buy me a coffeeBuying me a coffee
Jamie Taylor
A .NET developer specialising in ASP.NET MVC websites and services, with a background in WinForms and Games Development. When not programming using .NET, he is either learning about .NET Core (and usually building something cross platform with it), speaking Japanese to anyone who'll listen, learning about languages, writing for his non-dev blog, or writing for a blog about video games (which he runs with his brother)