Custom middlewares in ASP .NET Core 5

Custom middlewares in ASP .NET Core 5

The world of .NET Core is not new to most of us. We have been hearing about it since last few years and many of us already started using it.

ASP .NET Core web applications have concept of middleware. A middleware intercepts HTTP requests sent to .NET Core web application. In this article, let’s have a look at different ways to create custom middleware in an ASP .NET Core Web Application.

Create Web App

Although I am using .NET 5, the article is also applicable for .NET Core 3.1. So, if you do not have .NET 5, you can select ASP .NET Core 3.1 in the dropdown.

If you are using Visual Studio 2019 v16.8 preview 3, then you can create the ASP .NET Core web application in Visual Studio 2019.

ASP .NET Core 5 Web Application
ASP .NET Core 5 Web Application

For this demo, I am selecting Empty option from the list. Because mostly we will be playing around the Startup.cs file.

Alternatively, you can use below script to create the empty ASP .NET Core web application using .NET CLI .

## Create Proejct Directory
mkdir FirstWebApp
## Make it working directory
cd FirstWebApp
## To Trust the ASP .NET Core HTTPS Development Certificate (Windows and MacOS only)
dotnet dev-certs https –trust
## Creates Empty Web App project with same name as workign directory
dotnet new web
## Open Visual Studio Code to modify the code
Code .
## After making changes to Startup.cs
## Run the web app
dotnet run
view raw dotnet.sh hosted with ❤ by GitHub

What is Middleware ?

Before proceeding to create a new middleware, let’s first understand what is a request delegate and what is a middleware.

As per documentation, a Middleware is a piece of code that resides in the applications pipeline to handle requests and responses.

So, every middleware can perform below actions:

  • process incoming intercept HTTP requests,
  • decide if the request should be handed over to the next middleware or just end the response
  • process outgoing responses

Request Delegate vs Middleware

A request delegate is configured in the Startup to build the request pipeline. A request delegate can be configured in the pipeline using RunMap, and Use extension methods. 

Request delegate can then use either inline anonymous method or a reusable class for processing requests and responses. This inline anonymous method (in-line middleware) or reusable class is called as middleware or middleware component.

Examples

The middlewares are configured in Configure method of Startup.cs. For any real world site, there can be one or more than one middlewares configured there.

The .NET Core framework has default implementations for many common middlewares and when you create a ASP .NET Core web project, they are automatically configured in Startup.cs. Some common examples of middleware are

  • Authorization and Authentication
  • Static Files Middleware
  • Exception Handling Middleware
  • Routing Middleware

Custom Middlewares

If you want to add any custom logic in the application pipeline, you can do that by writing custom middleware. As stated earlier, there are three different ways to configure middleware,  RunMap, and Use extension methods. Let’s see how to use them.

Run

So, in the newly created web application project, go to the Startup.cs file and delete everything from Configure method. Using Run extension method, you can easily write simple inline middleware as shown below:

public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// Run method, anonymous inline middleware,
// Terminal middleware as it ends the app pipeline.
// NOTE: Run method does not have ability to call next middleware.
app.Run(async context =>
{
await context.Response.WriteAsync("My First .NET 5 Web App");
});
}
}
view raw Startup.cs hosted with ❤ by GitHub

The above middleware code just return the same response to every request. Note, that there is no provision to call next middleware. If another  Use  or  Run delegate is added after the Run delegate, it’s not called.

Use

The use method for registering the request delegate provides an additional parameter – namely next. Using this parameter, the next request delegate from the pipeline can be invoked.

Below is the example of how it works.

public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use(async (context, next) =>
{
// Do work that doesn't write to the Response.
// e.g. change something in context
// or log something to database
// Invoke next middleware
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
});
app.Run(async context =>
{
await context.Response.WriteAsync("My First .NET 5 Web App");
});
}
}
view raw Startup.cs hosted with ❤ by GitHub

Caution

Please note that writing anything to response is not allowed in the execution flow which invokes next middleware.

For ex, let’s say you have a middleware which checks if user has right to allow specific operation. The middleware can have an IF block to check the rights and stop the flow if the user does not have right. When the middleware decides to stop the flow, it should either redirect user to some error page without running next middleware. The same middleware can decide to invoke next middleware if user has expected rights.

If the middleware tries to change any contents of response in the same execution flow which invokes next middleware, there are chances that there might be corrupted HTML body or there might be a protocol violation errors as per documentation.

Measure Execution Interval

Below is a simple example of how middleware can be used to log and understand how much time is taken for a request processing. It assigns a unique identifier to every incoming request and uses Stopwatch class to count how much time was taken by the application to process one request.

It logs start of request processing and end of request processing events to a file.

public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.Use(async (context, next) =>
{
// Do work that doesn't write to the Response.
// e.g. change something in context
// or log something to database
using StreamWriter sw = File.AppendText("some-log-file.txt");
Stopwatch timer = new Stopwatch();
var startTime = DateTime.Now;
var requestId = Guid.NewGuid();
sw.WriteLine("{0}: Request processing started at: {1}", requestId, startTime.ToString("MMM/dd/yyyy hh:mm:ss.ffffff tt"));
timer.Start();
// Invoke next middleware
await next.Invoke();
// Do logging or other work that doesn't write to the Response.
timer.Stop();
var endTime = DateTime.Now;
sw.WriteLine("{0}: Request processing ended at: {1}", requestId, endTime.ToString("MMM/dd/yyyy hh:mm:ss.ffffff tt"));
sw.WriteLine("{0}: Ticks taken by request processing ended at: {1}", requestId, timer.Elapsed.Ticks);
});
app.Run(async context =>
{
await context.Response.WriteAsync("My First .NET 5 Web App");
});
}
}
view raw Startup.cs hosted with ❤ by GitHub

Map

There is one more extension method – Map – which we have not discussed in this article. I will write about it in the next article.

I hope this information was useful for you. Let me know your thoughts.

Leave a Reply