Matthew Jones

One of the new features from ASP.NET Core 1.0 is the idea of Middleware. Middleware are components of an application that examine the requests responses coming in to and going out from an ASP.NET Core application. It's a long-overdue piece of customization for ASP.NET apps that gives us developers total control over the HTTP pipeline. It might even help you avoid endless questions from annoying coworkers (maybe!).

As part of my ongoing learning process about ASP.NET Core, I decided to build a sample application that has a few pieces of middleware, so that I could play around with order, tasks, and so forth. It helped me understand what Middleware does, and why it might be important, so hopefully it'll help you too. Let's get started!

NOTE: This post was written using RC1 (Release Candidate 1) of ASP.NET Core, and as the framework gets closer to final release, some things may change. If they do, I'll do my best to keep this post up-to-date. If you notice anything that needs to be updated, let me know in the comments.

What Is Middleware?

Middleware, as explained above, are components that "sit" on the HTTP pipeline and examine requests and responses. This means that they are effectively "gateway" classes that can decide whether or not to continue allowing the request to be processed by other Middleware components further down the stream. The pipeline looks like this:

...decouple server and application, encourage the development of simple modules for .NET web development, and, by being an open standard, stimulate the open source ecosystem of .NET web development tools.

Considering their ideas have been fully integrated into ASP.NET Core 1.0, I'd say they've more than achieved this goal.

At any rate, Middleware defines a system by which we can exert complete control over the HTTP pipeline into and out of our apps. It effectively replaces the functionality that was formerly covered by things like HttpModules and HttpHandlers.

In this method, any time you see app.UseX(), you are using some of ASP.NET's default Middleware components. The concept of Middleware and the HTTP pipeline is central to how ASP.NET Core apps are developed. You can check out the other built-in Middleware by reading the docs.

Defining a Custom Middleware Component

A Middleware component, in an ASP.NET Core project, is a class like any other. The difference is that the Middleware component needs to have a private property of type RequestDelegate, like so:

At the moment, this method does nothing other than call the next Middleware component in the pipeline. Let's see a simple task that we can do with our new Middleware component.

Middleware Tasks

Since each piece of Middleware can examine the incoming request and the corresponding response, one possible task Middleware could accomplish is that of authorization.

For a (trivial) example, let's say that each request must NOT have a header "X-Not-Authorized", and if it does, the response must be returned immediately with a 401 Unauthorized status code. Our Middleware component's Invoke method would now look like this:

If we call any method in our app with a tool such as Postman, we'll get the following response:

Middleware can accomplish many sorts of things. For example:

You might want to examine each incoming request to see if they have requested an image, and if they have, redirect them to an image handler, much like you would when we used HttpHandlers.

You might have a component that logs all requests.

You might have a component that checks to see if you annoying coworker Doug from down the hall is making a request, and if he is, redirect all his requests to Mary just so you don't have to deal with him anymore.

The possibilities are endless!

Other Example Middleware

For our sample project, we've already got one Middleware component (AuthorizationMiddleware) defined. Now we will define two more. First up is RequestHeaderMiddleware:

Another way, and the way we're going to use, is to create extension methods for each piece of Middleware you want to register, and then call those methods from the Configure() method. Here's our Extensions class:

When you are registering Middleware with your environment, the order in which you add the Middleware components is very important. In the above code, we add ProcessingTimeMiddleware first, then RequestHeaderMiddleware, then AuthorizationMiddleware. This is the exact opposite order of what we should be using. The correct order looks like this (simplified):

Now let's make some Postman calls. First, let's make a call with no headers at all.

This is what we would expect to see. Neither the Authorization nor the RequestHeader Middleware components prevents execution, so the ProcessingTime component added the header and we got back the Action's 200 OK response.

Now let's see what happens if we add in the X-Not-Authorized header:

Great! The AuthorizationMiddleware component returned 401 Unauthorized, and because there's no "X-Processing-Time-Milliseconds" header isn't included in the response, the ProcessingTimeMiddleware component didn't fire. This is what we expect.

I'm sure you see where this is going by now. For the last test, let's see a request without "X-Not-Authorized" header but including "X-Transfer-By":

In this case, we see that both "X-Processing-Time-Milliseconds" and "X-Transfer-Success" returned, which means that both RequestHeaderMiddleware and ProcessingTimeMiddleware successfully fired. Looks like our scenarios work!

Let's try one more request.

Perfect!

Summary

Remember these three major points about Middleware in ASP.NET Core 1.0:

Middleware components allow you complete control over the HTTP pipeline in your app.

Among other things, Middleware components can handle authorization, redirects, headers, and even cancel execution of the request.

The order in which Middleware components are registered in the Startup file is very important.

You can see the sample code for this project in the GitHub repository. As always, let me know what you think about this post in the comments, particularly if I missed something important.