How to add custom middleware in ASP.NET Core

By FoxLearn 2/4/2025 8:29:12 AM   22
In ASP.NET Core, the request pipeline is a series of middleware components that handle HTTP requests.

These components, which can either be classes or lambdas, process requests in a specific sequence. While ASP.NET Core provides many built-in middleware options (such as AuthorizationMiddleware and HttpsRedirectionMiddleware), developers can also create custom middleware to handle specific needs.

Step 1: Create a Middleware Class

To create custom middleware in ASP.NET Core, you’ll typically follow a specific pattern:

  • The class should have a constructor that accepts a RequestDelegate parameter (to invoke the next middleware in the pipeline) and any dependencies needed.
  • The class should have an InvokeAsync() method that accepts an HttpContext parameter. This method can then perform three key tasks:
    1. Inspect or modify the request via HttpContext.Request.
    2. Call the next middleware using the RequestDelegate.
    3. Inspect or modify the response via HttpContext.Response.

For example, how to create a middleware class that logs both request and response headers:

public class HeadersMiddleware
{
    private readonly RequestDelegate _nextMiddleware;
    private readonly ILogger _logger;

    public HeadersMiddleware(RequestDelegate nextMiddleware, ILogger<HeadersMiddleware> logger)
    {
        _nextMiddleware = nextMiddleware;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 1. Inspect the request
        if (context.Request.Headers.TryGetValue("Debug", out var debug))
        {
            _logger.LogInformation($"Has Debug request header: {debug}");
        }

        // 2. Call the next middleware in the pipeline
        await _nextMiddleware(context);

        // 3. Inspect the response
        if (context.Response.Headers.TryGetValue("DebugInfo", out var debugInfo))
        {
            _logger.LogInformation($"Has DebugInfo response header: {debugInfo}");
        }
    }
}

In this example, the HeadersMiddleware class logs the presence of specific headers in both the request and response. You can modify this code to suit your needs, such as logging additional information or performing custom actions.

Step 2: Register the Middleware Using app.UseMiddleware()

Once your custom middleware is created, you need to register it in the application's request pipeline. This is done using the app.UseMiddleware<T>() method in the Program.cs or Startup.cs file, depending on the version of ASP.NET Core you're using.

For example, how to register the HeadersMiddleware in the pipeline:

// Register services and other middleware
var app = builder.Build();

app.UseHttpsRedirection(); // Adds HttpsRedirectionMiddleware
app.UseAuthorization(); // Adds AuthorizationMiddleware

// Register the custom middleware
app.UseMiddleware<HeadersMiddleware>();

// Further middleware registrations

app.Run();

In versions prior to .NET 6, middleware is registered in the Configure method of Startup.cs.

The order in which middleware is registered matters. Middleware components are executed in the order they are added to the pipeline. In the example above, HeadersMiddleware is added at the end of the pipeline, meaning it will run after the built-in middlewares like HttpsRedirectionMiddleware and AuthorizationMiddleware.

To test that your custom middleware is functioning as expected, you can send a request using a tool like Postman. Below is an example of the log output from the HeadersMiddleware:

info: HeadersMiddleware[0]
      Has Debug request header: enabled
info: HeadersMiddleware[0]
      Has DebugInfo response header: DB took 5 seconds

This shows that the middleware is correctly inspecting and logging the headers from both the request and the response.

When middleware components are registered using app.UseMiddleware(), their order in the pipeline matters. Middleware is executed in the same order in which it is registered. To better understand how this works, let’s consider a scenario where we register three middleware components:

app.UseMiddleware<FirstMiddleware>();
app.UseMiddleware<SecondMiddleware>();
app.UseMiddleware<ThirdMiddleware>();

In each of these middleware components, let’s assume that the InvokeAsync() method logs a message whenever it handles a request or response. The order of execution for a request would look like this:

FirstMiddleware got request. Calling next middleware.
SecondMiddleware got request. Calling next middleware.
ThirdMiddleware got request. Calling next middleware.
ThirdMiddleware got response.
SecondMiddleware got response.
FirstMiddleware got response.

As you can see, the InvokeAsync() methods are called in the order in which the middleware is registered. The request flows through the middleware in a top-down order, while the response flows bottom-up.

Custom middleware in ASP.NET Core allows you to extend the request pipeline to meet the specific needs of your application. By creating a custom middleware class and registering it using app.UseMiddleware(), you can add functionality such as logging, request modification, or response handling.

Remember that the order in which middleware is registered is crucial, as it dictates the sequence of execution during both the request and response phases.