ASP.NET Core – How to add custom middleware

The ASP.NET Core request pipeline has middleware components (classes or lambdas) for processing requests. There are many built-in middleware components, such as AuthorizationMiddleware and HttpsRedirectionMiddleware. You can create custom middleware. I’ll show how below.

1 – Create middleware class

In this example, I’ll show how to create a middleware class which is based on the following convention:

  • A constructor that accepts a RequestDelegate parameter (+ any dependencies).
  • A method called InvokeAsync() which accepts an HttpContext parameter. This has three steps:
    • Inspect/modify the request (via HttpContext.Request).
    • Call the next middleware (via the RequestDelegate).
    • Inspect/modify the response (via HttpContext.Response).

Here’s an example of creating a convention-based middleware class. This simply inspects the request and response headers and logs their values.

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 StringValues debug))
        {
            Logger.LogInformation($"Has Debug request header: {debug}");
        }

        //2 - Call the next middleware
        await NextMiddleware(context);

        //3 - Inspect the response
        if (context.Response.Headers.TryGetValue("DebugInfo", out StringValues debugInfo))
        {
            Logger.LogInformation($"Has DebugInfo response header: {debugInfo}");
        }
    }
}
Code language: C# (cs)

Read more about how you’d unit test this custom middleware code.

2 – Use app.UseMiddleware()

To actually use the middleware class, register it with app.UseMiddleware() in the initialization code:

//rest of adding services

var app = builder.Build();

app.UseHttpsRedirection(); // adds HttpsRedirectionMiddleware class

app.UseAuthorization(); // adds AuthorizationMiddleware class

app.UseMiddleware<HeadersMiddleware>(); // adds our custom HeadersMiddleware class

//rest of adding middleware

app.Run();
Code language: C# (cs)

Note: Before .NET 6, do this in Startup.Configure().

The order you add the middleware is the order it will run in the request pipeline (I’ll discuss this further down). In this example, it’s adding HeadersMiddleware to the end of the request pipeline, which means it’ll run last.

3 – Run it

To see the middleware working, send a request with Postman. Here’s an example of what the HeadersMiddleware logs:

info: HeadersMiddleware[0]
      Has Debug request header: enabled
info: HeadersMiddleware[0]
      Has DebugInfo response header: DB took 5 secondsCode language: plaintext (plaintext)

Middleware are used in the order they are registered

When you call app.UseMiddleware(), you’re registering the middleware component. The order you register middleware determines when it’s used.

To show an example of this, let’s say you have three middleware classes and you register them as follows:

app.UseMiddleware<FirstMiddleware>();
app.UseMiddleware<SecondMiddleware>();
app.UseMiddleware<ThirdMiddleware>();
Code language: C# (cs)

These middleware classes simply log that they got the request and response. When a request comes in, here’s the output:

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 responseCode language: plaintext (plaintext)

This shows the order of execution. The middleware InvokeAsync() methods get called in the order they are registered in. Keep this in mind when you’re deciding where to register your custom middleware.

Leave a Comment