How to create and add custom middleware in ASP.NET Core

Published: Monday 4 March 2024

The great thing about ASP.NET Core is that it provides a number of built-in middleware components.

These include a rate-limiter where we can add a restriction on a number of HTTP requests that can be made based on certain parameters.

Another one is exception handling where the HTTP response can be written when a certain exception is thrown within the application.

However, there may be instances where a custom one needs to be added.

For example, there may be an instance where additional response headers need to be added to the HTTP request.

Creating a custom middleware

As part of the WebApplication type used in Program.cs, there is a Use extension method. This extension method requires two parameters:

  • context - An instance of the HttpContext type
  • next - Runs the next method in the middleware chain

As we have an instance of the HttpContext, we are able to make changes to the response before it has started to be written.

In this example, we are adding a new HTTP response header stating that the X-Frame-Options header should be set to SAMEORIGIN.

// Program.cs
app.Use(async (context, next) =>
{
	context.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); 

	await next();
});

This is a security feature that ensures that iframes can only be loaded in the same origin.

Cruicially, the next parameter is invoked after it. What happens if we don't include that?

Short-circuiting

If we don't invoke the next parameter, then none of the other middleware methods in the chain will be run. 

// Program.cs
app.Use(async (HttpContext context, Func<Task> next) =>
{
	context.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN");
	//await next();
});

This is known as short-circuiting, or terminal middleware. If we run a Web API request in this instance, the X-Frame-Options header would get added to the HTTP response, but there is a good chance that the Web API response would not get returned.

Terminal middleware prevents an ASP.NET Core Web API response being returned

Terminal middleware prevents an ASP.NET Core Web API response being returned

Terminal middleware can also be used by calling app.Run. This might be a good idea if we want to write our own holding page as it passes in the HttpContext type as the parameter.

// Program.cs
app.Run(async (context) =>
{
	await context.Response.WriteAsync("<p>Holding page</p>");
});

Writing to the response after next has been called

If writing to the HTTP response, it's generally a good idea to write to it before calling the next() method.

By doing it afterwards, there is a high chance that the HTTP response would have already started and therefore would throw unexpected behaviour and hidden exceptions.

// Program.cs
app.Use(async (HttpContext context, Func<Task> next) =>
{
	await next();

	context.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN");
});

In this instance, if we executed an ASP.NET Core Web API endpoint, the X-Frame-Options would not be added to the HTTP response headers as the HTTP response has already started.

Custom middleware in it's own class

As well as using app.Use, custom middleware can be written to it's own class.

// HeaderMiddleware.cs
public class HeaderMiddleware
{
	private readonly RequestDelegate _next;

	public HeaderMiddleware(RequestDelegate next)
	{
		_next = next;
	}

	public async Task Invoke(HttpContext httpContext)
	{
		httpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN");

		await _next(httpContext);
	}
}

A next parameter is passed into the constructor which is stored as a private readonly field.

Then we add an Invoke method, passing in an instance of HttpContext. We add our X-Frame-Options header to the HTTP response with a value of SAMEORIGIN before invoking the next method passing in the HttpContext instance as a parameter.

The final thing we must do is register it to the WebApplication instance in Program.cs. We do that by calling the app.UseMiddleware extension method:

// Program.cs
app.UseMiddleware<HeaderMiddleware>();

Watch our video

Watch our video where we show how to add custom middleware to an ASP.NET Core Web API and demonstrate what happens with short-circuiting and writing to the HTTP response after the next middleware in the chain has been invoked.