Understanding Middleware in ASP.NET Core

Middleware is responsible for processing HTTP messages in ASP.NET Core applications. In this post, we define the middleware pipeline, explore a real example, and look at how to create custom middleware components.

The Middleware Pipeline

Middleware is responsible for processing HTTP messages. Let’s illustrate this with a very simple middleware pipeline.

Imagine a middleware pipeline consisting of three elements. Once an HTTP request is received, it will be inspected by the first piece of middleware. If it doesn’t have any response to return or any exception to throw, it passes the request on to the next component in the pipeline. Again, after inspecting the request, this component will either return a response, throw an exception, or pass the request along to the following piece of middleware. Suppose it does pass it along. Imagine this third and last component of this pipeline is responsible for finding something inside of the application that can process the request. If this was a POST request for a certain URL, it would go and look for a Razor Page in the folder with the same name as the URL in the request. If it finds the file, it will pass the request on to that Razor Page, which will generate a response in the form of HTML.

At this point, it is important to note that the middleware pipeline is bi-directional. When the request enters the pipeline, it will flow through the different components of the pipeline until one of them can produce a response, or an exception is thrown. Once this happens, the response will go through the pipeline again in the opposite direction, being inspected by the same components that the request went through on the way in.

So, in our example, once this third piece of middleware has emitted a response, this will be passed back onto the second component, and to the first one after. Most components won’t act after inspecting the response, but some might be in charge of recording information about the process or rendering an exception thrown by one of the components in the pipeline, if that was the case. We will later see some concrete examples of middleware components and what they do.

It is important to note that the order in which the middleware is placed inside of the Configure method in the Startup class will determine the order in which the request will invoke the different components of the middleware pipeline. The order of the components of the pipeline will affect the performance of the app, as we will see later.

A Concrete Example

Now that we have described how the middleware pipeline works, let’s look at a real example of middleware in an ASP.NET Core web application. When building a new web application, Visual Studio adds a middleware pipeline by default.

Below is the Configure method in the Startup class in a new ASP.NET Core web app.

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseAuthorization();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Let’s look at what each of these middleware components do.

DeveloperExceptionPage:
In order to understand what these components do, let’s remind ourselves again that the middleware pipeline is bi-directional. This means that the first component of the pipeline will be both the first to inspect the incoming HTTP request and the last to inspect the response. This will condition what we decide to place at the very beginning of the pipeline (or at the very end). In this case, DeveloperExceptionPage has been placed at the top because it cares about being the last to inspect the response. Its task is to handle exceptions being thrown by any other component in the pipeline.

When DeveloperExceptionPage receives an HTTP request, it will simply pass it on to the next component in the pipeline. If any subsequent component in the pipeline throws an exception, it will be passed back to the previous components of the pipeline and will finally reach DeveloperExceptionPage. DeveloperExceptionPage is in charge of rendering HTML containing detailed information on the exception, which will be useful for developers when debugging the problem.

ExceptionHandler:
Note that DeveloperExceptionPage is only used in development. When running in production, the level of detail rendered would be not only unnecessary, but could also expose sensitive information to users. When in production, ExceptionHandler will be used instead. ExceptionHandler operates in the same way as DeveloperExceptionPage, but the information displayed when an exception has been thrown is less detailed and more user friendly.

Hsts:
Hsts is also only used in production. This will check that the browser sending the request is operating over a secure connection.

HttpsRedirection:
This will redirect any requests using plain HTTP to an HTTP URL.

StaticFiles:
The StaticFiles middleware will attempt to serve a request by responding to a file that’s in the wwwroot folder of the application.

Routing and Endpoints:
When UseRouting and UseEndpoints are used in the middleware pipeline, this means that ASP.NET Core is performing EndPoint routing. This is where routing is implemented splitting the routing middleware into two separate middleware components: Routing and Endpoints. An endpoint is the object that contains the information needed to execute a request. So here, the Routing middleware matches the HTTP request to an endpoint first and then the Endpoints middleware executes the matched endpoint.

Authorisation:
In between the Routing and Endpoints middleware, Authorisation will check whether the browser is authorised to access the information it is requesting.

If any of these pieces of middleware can’t fulfil their purpose, they will throw an exception that will be handled by either DeveloperExceptionPage or ExceptionHandler, depending on whether we’re running in development or production.

Post a Comment

0 Comments