What is Middleware in .Net Core

ยท

3 min read

Middleware is a very broad term. In this article, we will go into a bit more detail about what is middleware and what are its uses in Asp.net Core.

Definition

Middleware is a piece of software that can handle an HTTP request or response. Any given middleware component has a very specific purpose e.g. authenticating a user, error handling or serving static files e.g. css or javascript files. Middleware components are chained together in the startup.cs file to set up a request processing pipeline. Specifically, this is within the configure method in the startup class. This, in turn, will determine how a request is processed.

How does middleware work?

In Asp.net Core, Middleware has access to incoming requests and outgoing responses. What this means is that a middleware component may process an incoming request and then pass that request to the next middleware component for further processing.

Below is an image from MSDN that explains the middleware pipeline.

image.png

For example, we can have a logging middleware that will log out the time the request was received and then pass on the request to the next middleware e.g. staticFiles middleware for example for further processing.

A middleware component can short-circuit the request pipeline if it decides not to call the next middleware component in the pipeline. This is good because it often will avoid unnecessary processing. For example, if the request is for a static file e.g. an image, the staticFiles middleware could handle this request and short-circuit the rest of the pipeline.

Additionally, a middleware component may choose to ignore an incoming request and pass the request to the next piece of middleware for further processing.

So it is these middleware components that determine how a request is processed. These components are executed in the order they are present in the startup.cs file so developers should be careful and thoughtful in where they add these components in the pipeline, ensuring they are in the correct processing order.

Middleware components are available as NuGet packages which allows us to update these components separately depending on what is required at the time. Custom middleware can also be built into your application.

There are no restrictions on how many middleware components you can have in your application. It all depends on your application requirements. For example, simple web applications may only need static files middleware, whereas more complex applications may need logging, authentication, MVC, authorisation middleware and more. Do not add middleware just for the sake of it, only add what you need to keep your application working efficiently with minimal processing power.

Middleware to call next middleware in the pipeline

If you wanted to register a middleware component that calls the following middleware component in the pipeline then you can register it using the "Use" method as shown below:

app.Use(async (context, next) => {
  await context.Response.WriteAync("Calling next!");
  await next();
});

By invoking the next delegate it will automatically call the next piece of middleware in the pipeline.

Terminal Middleware

If a middleware component that is registered using the "Run" method becomes a terminal piece of middleware. This is because it does not pass the processing to any further middleware components in the pipeline. When the "Run" method is called it will process the response and return the request. Any subsequent middleware in the pipeline is never called.

An example of terminal middleware is as follows:

app.Run(async (context) => {
  await context.Response.WriteAync("Success!");
});

At this point, the pipeline reverses itself (as seen in the diagram above) and any additional processing from earlier middleware components can occur here. By this, I am referring to any logic that is placed under the next() delegate method calls. In the example below, "I am returning a response" will be logged out once the pipeline has reversed and returned to this middleware component:

app.Use(async (context, next) => {
  await context.Response.WriteAync("Calling next!");
  await next();
  logger.LogInformation("I am returning a response"); 
});

Hope this was helpful.

Until next time ๐Ÿ™‹โ€โ™‚๏ธ.

Did you find this article valuable?

Support Michael Asaad by becoming a sponsor. Any amount is appreciated!

ย