An Absolute Beginner’s Tutorial on Middleware in ASP.NET Core/MVC (and writing custom middleware)

In this article, we will try to understand the concept of middleware in ASP.NET core. We will see how middleware plays an important part in request response pipeline and how we can write and plug-in our custom middleware.

Background

Before we could get into the what middleware is and the value it brings, we need to understand how the request response worked in classic ASP.NET model. In earlier days the request and response objects in ASP.NET were very big and had very tight coupling with IIS. This was a problem because some of the values in these objects are filled by the IIS request-response pipeline and unit testing such bloated objects was a very big challenge.

So the first problem that needed to be solved was to decouple the applications from web servers. This was very nicely defined by a community owned standards called Open Web Interface for .NET (OWIN). Since the older ASP.NET applications were dependent on System.Web DLL which internally had very tight coupling with IIS, it was very difficult to decouple the applications from web servers. to circumvent this problem OWIN defines is to remove the dependency of web applications on System.web assembly so that the coupling with web server(IIS) gets removed.

OWIN primarily defines following actors in its specifications:

  • Server — The HTTP server that directly communicates with the client and then uses OWIN semantics to process requests. Servers may require an adapter layer that converts to OWIN semantics.
  • Web Framework — A self-contained component on top of OWIN exposing its own object model or API that applications may use to facilitate request processing. Web Frameworks may require an adapter layer that converts from OWIN semantics.
  • Web Application — A specific application, possibly built on top of a Web Framework, which is run using OWIN compatible Servers.
  • Middleware — Pass through components that form a pipeline between a server and application to inspect, route, or modify request and response messages for a specific purpose.
  • Host — The process an application and server execute inside of, primarily responsible for application startup. Some Servers are also Hosts.

Since OWIN is just a standard, there are multiple implementation for this in last few years starting from Katana to the present day implementation in ASP.NET core. We will now focus on how the middleware implementation looks like in ASP.NET core.

Before that, lets try to understand what a middleware is. For the developer coming from ASP.NET world, the concept of HTTPModule and HTTPHander is fairly familiar. These are used to intercept the request-response pipeline and implement our custom logic by writing custom modules or handlers. In the OWIN world, the same thing is achieved by the middleware.

OWIN specifies that the request coming from web server to the web application has to pass through multiple components in a pipeline sort of fashion where each component can inspect, redirect, modify or provide a response for this incoming request. The response then will get passed back to the web server in the opposite order back to the web server which then can be served back to the user. Following image visualizes this concept:

If we look at the above diagram we can see that the request passes through a chain of middleware and then some middleware decides to provide a response for the request and then response travels back to web server passing through all the same middleware it passed through while request. So a middleware typically can:

  • Process the request and generate the response.
  • Monitor the request and let it pass thorough to next middleware in line.
  • Monitor the request, Modify it and then let it pass through to next middleware in line.

If we try to find the middleware with actual use cases defined above:

  • Process the request and generate the response: MVC itself is a middleware that typically gets configured in the very end of middleware pipeline
  • Monitor the request and let it pass through to next middleware in line: Logging middleware which simply logs the request and response details
  • Monitor the request, Modify it and then let it pass through to next middleware in line: Routing and Authentication module where we monitor the request decide which controller to call(routing) and perhaps update the identity and Principle for authorization(Auth-Auth).

Using the code

In this article, we will create 2 owin middleware. First one to demonstrate the scenario where we are not altering the request. for this we will simply log the request and response time in the log – TimingMiddleware. Second one to check the incoming response, find a specific header value to determine which tenant is calling the code and then returning back if the tenant is not valid – MyTenantValidator.

Note: Before we get started with the sample implementation, its good to highlight the point that middleware is an implementation of pipes and filter pattern. Pipes and filter pattern says that if we need to performs a complex processing that involves a series of separate activity, its better to separate out each activity as a separate task that can be reused. This gives us benefits in terms of reusability, performance and scalability.

Lets start by looking at how the middleware class definition should look like. There are two ways to define our custom middleware:

  1. Custom middleware class
  2. Inline custom middleware

Custom middleware class

First way is to have a custom class having containing our middleware logic.

What this class does is that it gets called once the request reached to this middleware. InvokeAsync function will get called and the current HttpContext will be passed to it. We can then execute our custom logic using this context and then call the next middleware in the pipeline. Once the request is processed by all middleware after this middleware, the response is generated and the response will follow the reverse chain and the function will reach after our _next call where we can execute put the logic that we want to execute before the response goes back to the previous middleware.

For our middleware to get into the pipeline, we need to use the Configure method in our Startup class to hook our middleware.

The above code shows how we have hooked in our custom middleware as the first middleware in the pipeline. The middleware will be called in the same order as they are hooked in this method. So in the above code our middleware will be called first and the MVC middleware will be the last one to get called.

Inline custom middleware

The inline custom middleware is directly defined in the Configure method. Following code shows how to achieve this:

The end result will be same for both approaches. So if our middleware is doing some trivial things that does not impact the readability of code if we put as inline, we could create the inline custom middleware. If the code that we want to significant code and logic in our middleware, we should use the custom middleware class to define our middleware.

Coming back to the middleware that we are going to implement, we will use the inline approach to define the TimingMiddleware and the custom class approach to define the MyTenantValidator.

Implementing the TimingMiddleware

The sole purpose of this middleware is to inspect the request and response and log the time that this current request took to process. Lets define it as inline middleware. Following code shows how this can be done.

We have hooked this middleware just before MVC middleware so that we can measure the time our request processing is taking. It is defined after UseStaticFiles middleware so that this middleware will not get invoked for all static files that are being served from our application.

Implementing the MyTenantValidator

Now lets implement a middleware that will take care of tenant verification. It will check for the incoming header and if the tenant is not valid, it will stop the request processing.

Note: For the sake if simplicity, I will be looking for a hard coded tenant id value. But in real world applications, this approach should never be used This is being done only for demonstration purpose. For not we will be using the tenant id value as 12345678.

This middleware will be written in its separate class. The logic is simple, check for the headers in incoming request. If the header matches the hard coded tenant id, let the request proceed to next middleware else terminate the request by sending response from this middleware itself. let look at the code of this middleware.

Now lets register this middleware in our Startup class.

With this code in place, if we try to run the application, we can see the response as error.

To circumvent this issue, we need to pass the tenant id in the header.

With this change, when we access the application again, we should be able to browse our application.

Note: Even though we were talking in context of ASP.NET core, the concept of middleware is same in all MVC implementations that are adhering to OWIN standards.

Point of interest

In this article, we talked about ASP.NET core middleware. We looked at what middleware are and how we can write our own custom middleware. This article has been written from a beginner’s perspective. I hope this has been somewhat informative.

References

Download the sample code for the article here: OwinTest

This site uses Akismet to reduce spam. Learn how your comment data is processed.