Inserting middleware between UseRouting() and UseEndpoints() as a library author - Part 2
Tags: middleware, middlewareanalysis, netcore30, aspnetcore
Published: 2020-02-09 11:25:21

---
title: Inserting middleware between UseRouting() and UseEndpoints() as a library author - Part 2
published: true
date: 2020-02-04 10:00:00 UTC
tags: Middleware,Middleware Analysis ,.NET Core 3.0,ASP.NET Core
canonical_url: https://andrewlock.net/inserting-middleware-between-userouting-and-useendpoints-as-a-library-author-part-2/
series: Inserting middleware between UseRouting() and UseEndpoints()
---

 ![Inserting middleware between UseRouting() and UseEndpoints() as a library author - Part 2](https://andrewlock.net/content/images/2020/conditional_middleware_after_banner.png)

This post follows on from [my previous post](https://andrewlock.net/inserting-middleware-between-userouting-and-useendpoints-as-a-library-author-part-1/) - if you haven't already, I strongly recommend reading that one first.

These posts are in response to a question from a reader about how library authors can ensure consumers of their library insert the library's middleware at the right point in their app middleware pipeline. In the previous post I showed an approach that allowed you to ensure your middleware runs before a given (named) piece of middleware, for example just before the `EndpointMiddleware` added via a call to `UseEndpoints()`.

One of the limitations of this approach is that you can only run your middleware _before_ a given named middleware. There's no way to run your middleware _after_ a named middleware. For example, maybe I want my middleware to run immediately _after_ the call to `UseRouting()`.

In my approach from the previous post, there's no way to achieve that - you can only select a middleware to run _before_. In this post I adapt the approach to allow you to specify which middleware you want to run after. I warn you now though, it's not very pretty…

> [Kamushek](https://disqus.com/by/disqus_5fuKnO8yCp/) pointed out a significant flaw in the approach taken in these posts - you can only have a single library use this approach to add middleware. That's a shame, but I'm not really sure I would recommend using these approaches anyway: they're more of an intellectual exercise!

## The middleware pipeline is built in reverse

In the previous post, I used the `RequestDelegate` passed in to our custom middleware's constructor to discover what the _next_ middleware is. The middleware pipeline is built up is by creating an instance of middleware, and obtaining a reference to it's `Invoke` method. This reference is used to create the `RequestDelegate` for the _previous_ middleware. So each middleware has a reference to the next middleware in the pipeline.

[That means the pipeline is built from the end first, working back to the start of the pipeline](https://github.com/aspnet/AspNetCore/blob/c7937640a4079465d36441724933eae5a9ca0085/src/Http/Http/src/Builder/ApplicationBuilder.cs#L82-L109). Each _earlier_ middleware "wraps" the rest of the pipeline.

<picture>
    <source srcset="/content/images/2020/middleware_pipeline_construction.svg" type="image/svg+xml">
    <img src="https://andrewlock.net/content/images/2020/middleware_pipeline_construction.png" alt="Image of how middleware is constructed">
  </source></picture>

This is a problem for us when we want to add middleware _after_ a given named middleware like the `EndpointRoutingMiddleware`. When the pipeline is being built, there's no way to know what the "previous" middleware in the pipeline will be - we're building the pipeline back-to-front, so it hasn't been created yet!

Of course at _runtime_ the opposite is true. Middleware _early_ in the pipeline can send signals to later middleware, for example by setting values in the `HttpContext.Items` collection, or via a service in the DI container.

In order to insert middleware at a specific point in your pipeline, we could use these two facts in combination, as I show in the next section.

## When in doubt, add moar middleware

I've described two features of the middleware pipeline that we can use to achieve our goal:

- At build time (and at runtime) a given middleware can inspect the type of the next middleware/`RequestDelegate` that will run 
- At runtime, middleware can send messages to _later_ middleware

The approach we'll use is:

- When explicitly adding middleware to the pipeline (in `Startup.Configure`), intercept the call, and add two extra piece of middleware - one before the target middleware (`NameCheckMiddleware`), and one after (`ConditionalMiddleware`). This is similar to the `AnalysisMiddleware` approach I showed in [the previous post](https://andrewlock.net/inserting-middleware-between-userouting-and-useendpoints-as-a-library-author-part-1/). 
- Record the name of the "wrapped" middleware at build time
- At runtime, pass the name of the wrapped middleware from the "pre-" middleware to the "-post" middleware
- If the name indicates it's the middleware we're looking for, run the extra functionality.

That's all a bit confusing - hopefully the example below makes more sense. In this example we want to run our middleware immediately after the call to `UseRouting()`.

<picture>
    <source srcset="/content/images/2020/conditional_middleware_after.svg" type="image/svg+xml">
    <img src="https://andrewlock.net/content/images/2020/conditional_middleware_after.png" alt="Name Check and Conditional middleware wraps all the other middleware, but the conditional middleware only executes its logic in one location">
  </source></picture>

Like I said at the start, it's not pretty. But it does work…

## Implementing the middleware

Hopefully you have a grasp of what we're trying to achieve here, so lets show some code. There's not really many additional concepts from last time, but I'll provide all the necessary code for completeness.

We'll start with the `NameCheckMiddleware`. This is the "pre-" middleware that records the name of the middleware being wrapped:

```csharp
internal class NameCheckerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly string _wrappedMiddlewareName;

    public NameCheckerMiddleware(RequestDelegate next)
    {
        // Store the name of the next middleware once at build time
        _wrappedMiddlewareName = next.Target.GetType().FullName;
        _next = next;
    }

    public Task Invoke(HttpContext httpContext)
    {
        // (Over)write the name of the wrapped middleware, for use by subsequent middleware
        httpContext.Items["WrappedMiddlewareName"] = _wrappedMiddlewareName;

        return _next(httpContext);
    }
}
```

The name of the `RequestDelegate` is calculated in the constructor, i.e. only once, when the middleware is constructed. In the `Invoke` method we set the middleware in the `HttpContext.Items` dictionary, for use by our "post-" middleware, the `ConditionalMiddleware` shown below:

```csharp
internal class ConditionalMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ConditionalMiddleware> _logger;
    private readonly string _runAfterMiddlewareTypeName;

    public ConditionalMiddleware(RequestDelegate next, ILogger<ConditionalMiddleware> logger, string runAfterMiddlewareName)
    {
        // Middleware we're looking for provided as constructor argument
        _runAfterMiddlewareTypeName = runAfterMiddlewareName;
        _next = next;
        _logger = logger;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        // Check if we're running after the middleware we want
        if(IsCorrectMiddleware(httpContext, _runAfterMiddlewareTypeName))
        {
            // If so, run the extra code - just logging as an example here
            _logger.LogInformation("Running conditional middleware after {PreviousMiddleware}", _runAfterMiddlewareTypeName);
        }

        // Either way, run the rest of the pipeline
        await _next(httpContext);
    }

    // Try and get the key added by the NameCheckerMiddleware and see if it's the one we need 
    static bool IsCorrectMiddleware(HttpContext httpContext, string requiredMiddleware)
    {
        return httpContext.Items.TryGetValue("WrappedMiddlewareName", out var wrappedMiddlewareName)
            && wrappedMiddlewareName is string name 
            && string.Equals(name, requiredMiddleware, StringComparison.Ordinal);
    }
}
```

The difference between this `ConditionalMiddleware` and [the version from my previous post](https://andrewlock.net/inserting-middleware-between-userouting-and-useendpoints-as-a-library-author-part-1/#inserting-middleware-before-useendpoints) is that we don't know at the middleware build-time whether a given `ConditionalMiddleware` instance is the one we need. Instead, we have to check the `HttpContext.Items` dictionary to see what the previous middleware was. If it's the one we're looking for, then we run the extra code.

To add these middleware to the pipeline we provide a custom `IApplicationBuilder`, just as we did in the last post. The code here is almost identical; the only difference is we're adding both the `ConditionalMidleware` _and_ the `NameCheckerMiddleware` in the `Use()` method:

```csharp
internal class ConditionalMiddlewareBuilder : IApplicationBuilder
{
    // The middleware we're looking for is provided as a constructor argument
    private readonly string _runAfter;
    public ConditionalMiddlewareBuilder(IApplicationBuilder inner, string runAfter)
    {
        _runAfter = runAfter;
        InnerBuilder = inner;
    }

    private IApplicationBuilder InnerBuilder { get; }

    public IServiceProvider ApplicationServices
    {
        get => InnerBuilder.ApplicationServices;
        set => InnerBuilder.ApplicationServices = value;
    }

    public IDictionary<string, object> Properties => InnerBuilder.Properties;
    public IFeatureCollection ServerFeatures => InnerBuilder.ServerFeatures;
    public RequestDelegate Build() => InnerBuilder.Build();
    public IApplicationBuilder New() => throw new NotImplementedException();

    public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
    {
        // Wrap the provided middleware with a name checker and conditional middleware
        return InnerBuilder
            .UseMiddleware<NameCheckerMiddleware>()
            .Use(middleware)
            .UseMiddleware<ConditionalMiddleware>(_runAfter);
    }
}
```

In order to use the custom builder, we can use the same `IStartupFilter` as in the last post:

```csharp
internal class ConditionalMiddlewareStartupFilter : IStartupFilter
{
    private readonly string _runAfterMiddlewareName;

    public ConditionalMiddlewareStartupFilter(string runAfterMiddlewareName)
    {
        _runAfterMiddlewareName = runAfterMiddlewareName;
    }

    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return builder =>
        {
            var wrappedBuilder = new ConditionalMiddlewareBuilder(builder, _runAfterMiddlewareName);
            next(wrappedBuilder);
        };
    }
}
```

Finally, a couple of extension methods to make adding the startup filter easy:

```csharp
public static class ConditionalMiddlewareExtensions
{
    public static IServiceCollection AddConditionalMiddleware(this IServiceCollection services, string afterMiddleware)
    {
        return services.AddTransient<IStartupFilter>(_ => new ConditionalMiddlewareStartupFilter(afterMiddleware));
    }

    public static IServiceCollection AddConditionalMiddlewareAfterRouting(this IServiceCollection services)
    {
        return services.AddConditionalMiddleware("Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware");
    }
}
```

The `AddConditionalMiddlewareAfterRouting()` method looks for the `EndpointRoutingMiddleware` added by the `UseRouting()` call. All that remains is to call the method in our app:

```csharp
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddConditionalMiddlewareAfterRouting(); // <-- Add this line
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseDeveloperExceptionPage();
        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();
        // <-- The ConditionalMiddleware will execute here

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}
```

Just as in the previous post, we only had to add one line, even though there's a _lot_ going on behind the scenes.

Again, this example is just a proof of concept - it doesn't do anything interesting other than print the following log message just after the `EndpointRoutingMiddleware`:

```
info: MyTestApp.ConditionalMiddleware[0]
      Running conditional middleware after Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware

```

The approach in this post will have slightly more impact at runtime than the approach in the previous post. While we can do the middleware name check up front at pipeline build time, we need to pass this name through the `HttpContext.Items` dictionary and add a check for every `ConditionalMiddleware` instance. This is obviously a pretty small penalty in the grand scheme of things, but it's still something I'd be hesitant forcing on consumers of my library.

As in my last post, as [highlighted by Kamushek](http://disq.us/p/26yd7u9), this technique only works once - you can't add multiple middleware in this way, as they will conflict with each other. That makes the approach of very limited usefulness in my book, but you never know!

## Summary

In this post I expanded on the problem posed in my previous post: how can a library author insert middleware at a specific point in a consuming app's middleware pipeline. In the previous post I discussed the approach when you need to place middleware _before_ a specific target middleware. In this post I discuss the slightly trickier task of placing middleware _after_ a target middleware.

The solution in this post requires wrapping each standard piece of middleware in two additional middleware instances. The first, `NameCheckerMiddleware` records the name of the wrapped middleware, and writes it to the `HttpContext.Items` dictionary. The subsequent `ConditionalMiddleware` checks this value to see if the target middleware (e.g. (`EndpointRoutingMiddleware`) was just executed, and if it was it executes its logic. The end result is definitely not pretty, but it works!