Inserting middleware between UseRouting() and UseEndpoints() as a library author - Part 1
Tags: middleware, middlewareanalysis, netcore30, aspnetcore
Published: 2020-02-01 09:18:57

---
title: Inserting middleware between UseRouting() and UseEndpoints() as a library author - Part 1
published: true
date: 2020-01-28 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-1/
series: Inserting middleware between UseRouting() and UseEndpoints()
---

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

This post is 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 the app's middleware pipeline. Unfortunately there's not really a great solution to that (with a couple of exceptions), but this post highlights one possible approach.

## Introduction: ordering is important for middleware

One of the big changes I discussed in [my recent series on ASP.NET Core 3.0](https://andrewlock.net/series/upgrading-to-asp-net-core-3/) is that [routing now uses Endpoint Routing by default](https://andrewlock.net/converting-a-terminal-middleware-to-endpoint-routing-in-aspnetcore-3/#the-evolution-of-routing). This manifests most visibly in your ASP.NET Core apps as two separate calls in the `Startup.Configure` method that configures the middleware pipeline for your app:

```csharp
public void Configure(IApplicationBuilder app)
{
    // ... middleware

    app.UseRouting();

    // ... other middleware

    app.UseEndpoints(endpoints =>
    {
        // ... endpoint configuration
    });
}
```

As shown above, there are two separate calls related to routing in a typical .NET Core 3.0 middleware pipeline: `UseRouting()` and `UseEndpoints()`. In addition, middleware can be be placed before, or between these calls. One thing that often trips people up in general is that **where you place your middleware is very important**. It's important you understand _what each piece of middleware is doing_ so that you can put it in the right point in the pipeline.

For example, in ASP.NET Core 3.0 it's important that you place the `AuthenticationMiddleware` and `AuthorizationMiddleware` _between_ the two routing middleware calls (and that you place authentication middleware before the authorization middleware):

```csharp
public void Configure(IApplicationBuilder app)
{
    // ... middleware

    app.UseRouting();

    app.UseAuthentication(); // Must be after UseRouting()
    app.UseAuthorization(); // Must be after UseAuthentication()

    app.UseEndpoints(endpoints =>
    {
        // ... endpoint configuration
    });
}
```

This requirement is [mentioned in the documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-3.1#endpoint-routing), but it's not very obvious. For example, [an issue Rick Strahl ran into](https://github.com/aspnet/AspNetCore/issues/14049) when [upgrading his Album Viewer sample to .NET Core 3.0](https://weblog.west-wind.com/posts/2019/Sep/24/Upgrading-my-AlbumViewer-Sample-to-NET-Core-30#gotcha-order-matters) was related to exactly this - middleware added in the wrong order.

> ASP.NET Core now does some checks to try and warn you at runtime when you have configured your pipeline incorrectly, as in the case described above. However it only catches a couple of cases, so you still need to be careful when building your pipeline.

This leads us to the heart of the question I received - if you're a library author, how can you ensure your middleware is added at the correct point in a consumer's middleware pipeline?

## Using IStartupFilter

The simplest scenario is where you need to add middleware to a user's app and you can add it near the _start_ of the middleware pipeline. For this use-case, there's `IStartupFilter`. I discussed `IStartupFilter` [in a previous post](https://andrewlock.net/exploring-istartupfilter-in-asp-net-core/) (over 3 years ago now, doesn't time fly!) but nothing has really changed about it since then, so I'll only describe it briefly here - check out [that previous post for a more detailed explanation](https://andrewlock.net/exploring-istartupfilter-in-asp-net-core/).

[`IStartupFilter`](https://github.com/aspnet/AspNetCore/blob/7ae6d1e3791e09db1fe2b032044208b04d4cc3b1/src/Hosting/Abstractions/src/IStartupFilter.cs) provides a mechanism for adding middleware to an app's pipeline by adding a _service_ to the DI container. When building an app's middleware pipeline, the ASP.NET Core infrastructure looks for any registered `IStartupFilter`s and runs them, essentially providing a mechanism for tacking middleware onto the beginning of an app's pipeline.

For example, [the `ForwardedHeadersStartupFilter`](https://github.com/aspnet/AspNetCore/blob/7ae6d1e3791e09db1fe2b032044208b04d4cc3b1/src/DefaultBuilder/src/ForwardedHeadersStartupFilter.cs) automatically adds [the `ForwardedHeadersMiddleware`](https://github.com/aspnet/AspNetCore/blob/7ae6d1e3791e09db1fe2b032044208b04d4cc3b1/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs) to the start of a middleware pipeline. This `IStartupFilter` [is (conditionally) added to the DI container by the `WebHost` on app startup](https://github.com/aspnet/AspNetCore/blob/1480b998660d2f77d0605376eefab6a83474ce07/src/DefaultBuilder/src/WebHost.cs#L240-L252), so you don't have to remember explicitly add it as part of your app's middleware configuration.

This approach is very useful in many cases, but has one significant limitation - you can only add middleware at the start (or end) of the pipeline defined in `Startup.Configure`. There's no simple way to add middleware in the middle.

Unfortunately, that requirement has likely become more common in ASP.NET Core 3.0 with endpoint routing and the separate `UseRouting()` and `UseEndpoints()` calls. `IStartupFilter` doesn't provide an easy mechanism for adding middleware at an arbitrary location in the pipeline, so you have to get inventive.

## Taking a lead from the AnalysisMiddleware

After initially dismissing the task as impossible, I had a small flashback [to some posts I wrote about the _Microsoft.AspNetCore.MiddlewareAnalysis_ package](https://andrewlock.net/tag/middleware-analysis-package/). This package can be used to log all the middleware that is executed as part of the middleware pipeline.

I explored how to use the package [in a previous post](https://andrewlock.net/understanding-your-middleware-pipeline-with-the-middleware-analysis-package/), and looked at how it was implemented [in another](https://andrewlock.net/under-the-hood-of-the-middleware-analysis-package/). Given we're going to use a similar approach to insert middleware between the calls to `UseRouting()` and `UseEndpoints()`, I'll give an overview of the approach here. For a more detailed understanding, [check out my previous posts](https://andrewlock.net/under-the-hood-of-the-middleware-analysis-package/).

The _MiddlewareAnalysis_ package uses an `IStartupFilter` to hook into the middleware configuration process of an app. But instead of just adding middleware to the start (or end) of the pipeline, it _wraps_ the `IApplicationBuilder` instance in a new type, the `AnalysisBuilder`:

```csharp
public class AnalysisStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return builder =>
        {
            var wrappedBuilder = new AnalysisBuilder(builder);
            next(wrappedBuilder);

            // There's a couple of other bits here I'll gloss over for now
        };
    }
}

```

The `AnalysisBuilder` implements `IApplicationBuilder`, and its purpose is to intercept any calls to `Use()` that add middleware to the pipeline. If you follow the method calls far enough down, all calls to `IApplicationBuilder` that modify the pipeline call `Use()`, whether it's `UseStaticFiles()`, `UseAuthentication()`, or `UseMiddleware<MyCustomMiddleware>()`.

When the app calls `Use` on the `AnalysisBuilder`, the builder adds it to the pipeline as normal, but it _first_ adds an extra piece of middleware, the `AnalysisMiddleware`:

```csharp
public class AnalysisBuilder : IApplicationBuilder
{
    private IApplicationBuilder InnerBuilder { get; }
    public AnalysisBuilder(IApplicationBuilder inner)
    {
        InnerBuilder = inner;
    }

    public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
    {
        return InnerBuilder
            .UseMiddleware<AnalysisMiddleware>()
            .Use(middleware);
    }
}

```

The end result is that an instance of `AnalysisMiddleware` is interleaved between all the other middleware in your pipeline:

![A middleware pipeline where the AnalysisMiddleware is added before every other middleware](https://andrewlock.net/content/images/2017/02/Analysis-Middleware.png)

The `AnalysisMiddleware` itself determines the name of the _next_ middleware in the pipeline in its constructor by interrogating the provided `RequestDelegate`:

```csharp
public class AnalysisMiddleware
{
    private readonly string _middlewareName
    public AnalysisMiddleware(RequestDelegate next)
    {
        _middlewareName = next.Target.GetType().FullName;
    }
    // ...
}

```

After looking through the code, I gleaned a few key points:

- We can create an `IStartupFilter`/`IApplicationBuilder` that can "bookend" each middleware added with an extra piece of middleware
- The `Type` of the _next_ middleware in the pipeline can be retrieved when the middleware is _processing_, but not when it's being constructed. i.e. you can get the name of the next middleware in the pipeline from `AnalysisMiddleware`, but not from the `AnalysisBuilder`.
- You can't easily get the name of the _previous_ middleware in the pipeline.

With this in mind, I set about finding a solution to the original problem, inserting middleware between the `UseRouting()` and `UseEndpoints()` from a class library.

## Inserting middleware before UseEndpoints

Given the final point raised in the previous section - it's not possible to check which middleware executed _previously_ to the current one, I decided the easiest location to insert middleware would be just before the `UseEndpoints()` call which adds the `EndpointMiddleware` to the pipeline.

The overall approach is _very_ similar to that used in the _MiddlewareAnalysis_ package. For this example I named the middleware `ConditionalMiddleware`, as it only runs under a single condition - when the next middleware is of a given type:

- Create an `IStartupFilter` that replaces the default `IApplicationBuilder` with a custom one, `ConditionalMiddlewareBuilder`, that intercepts calls to `Use(...)`
- Every time middleware is added to the pipeline, add an instance of the `ConditionalMiddleware` first. 
- When the `ConditionalMiddleware` executes, check if the _next_ middleware is the one we're looking for. If it is, run the additional logic before invoking the next middleware in the pipeline.

I'll start with the easy bit, the `ConditionalMiddleware` itself. For this example the "extra logic" we're going to execute is just to write out a log message. In practice you might use it to set a request feature or do some sort of request-specific check.

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

    public ConditionalMiddleware(RequestDelegate next, ILogger<ConditionalMiddleware> logger, string runBefore)
    {
        // Check if the next middleware is of the required type
        _runMiddleware = next.Target.GetType().FullName == runBefore;

        _next = next;
        _logger = logger;
        _runBefore = runBefore;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        // if the next middleware is the required type, run the exta logic
        if (_runMiddleware)
        {
            _logger.LogInformation("Running conditional middleware before {NextMiddleware}", _runBefore);
        }

        // either way, call the next middleware in the pipeline
        await _next(httpContext);
    }
}

```

In this example I'm passing in the name of the middleware that we want to run our custom logic before (i.e.`"EndpointMiddleware"` for my original example). We then check whether the next middleware is the one we're looking for. We can do the check in the constructor, as the middleware pipeline is fixed after it's built - the check ensures that the additional functionality is only run where we need it to be:

<picture>
    <source srcset="/content/images/2020/conditional_middleware.svg" type="image/svg+xml">
    <img src="https://andrewlock.net/content/images/2020/conditional_middleware.png" alt="Conditional middleware is added between all the other middleware, but only executes its logic in one location">
  </source></picture>

Next up is the `ConditionalMiddlewareBuilder`. This is the wrapper class that we use to inject our `ConditionalMiddleware` between each "real" middleware in the pipeline. It's mostly just a wrapper around the `InnerBuilder` provided in the constructor:

```csharp
internal class ConditionalMiddlewareBuilder : IApplicationBuilder
{
    // The middleware we're looking for is provided as a constructor argument
    private readonly string _runBefore;
    public ConditionalMiddlewareBuilder(IApplicationBuilder inner, string runBefore)
    {
        _runBefore = runBefore;
        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)
    {
        // Add the conditional middleware before each other middleware
        return InnerBuilder
            .UseMiddleware<ConditionalMiddleware>(_runBefore)
            .Use(middleware);
    }
}
```

The `ConditionalMiddlewareBuilder` is added to the application using an `IStartupFilter` that wraps the "original" `IApplicationBuilder` with our imposter:

```csharp
public class ConditionalMiddlewareStartupFilter : IStartupFilter
{
    private readonly string _runBefore;
    public ConditionalMiddlewareStartupFilter(string runBefore)
    {
        _runBefore = runBefore;
    }

    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return builder =>
        {
            // wrap the builder with our interceptor
            var wrappedBuilder = new ConditionalMiddlewareBuilder(builder, _runBefore);
            // build the rest of the pipeline using our wrapped builder
            next(wrappedBuilder);
        };
    }
}
```

Finally, lets create a couple of extension methods to make adding our new middleware easy:

```csharp
public static class ConditionalMiddlewareExtensions
{
    // Add ConditionalMiddlware that runs just before the middleware given by "beforeMiddleware"
    public static IServiceCollection AddConditionalMiddleware(this IServiceCollection services, string beforeMiddleware)
    {
        // Add the startup filter to wrap the middleware
        return services.AddTransient<IStartupFilter>(_ => new ConditionalMiddlewareStartupFilter(beforeMiddleware));
    }

    // A helper that runs the conditional middleware just before the call to `UseEndpoints()`
    public static IServiceCollection AddConditionalMiddlewareBeforeEndpoints(this IServiceCollection services)
    {
        return services.AddConditionalMiddleware("Microsoft.AspNetCore.Routing.EndpointMiddleware");
    }
}
```

With all that configured, the one thing that remains is to add the middleware to our app:

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

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

        app.UseRouting();

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

        // <-- The ConditionalMiddleware will execute here
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}
```

As you can see, we only had to add one line. This is great for library authors, as they don't have to deal with issues arising from users putting the middleware in the wrong place. And it's great for consumers because they don't have to worry about getting it wrong either! Just add the services to your DI container and you're good to go.

Of course, this example is just a proof of concept - it doesn't do anything interesting other than print the following log message just before the `EndpointMiddleware`, but it demonstrates an interesting technique:

```
info: MyTestApp.ConditionalMiddleware[0]
      Running conditional middleware before Microsoft.AspNetCore.Routing.EndpointMiddleware
```

The other good thing is that while this _looks_ complicated, and it adds a _lot_ of extra middleware, the impact at runtime should be minimal. The "next middleware check" is only executed once per middleware instance, and when the evaluation returns `false` the runtime effect will be very small - a single additional `if` check per middleware. Still, I haven't found myself needing to do something like this before, so I'd be interested to hear if someone tries it and how they get on!

## Summary

In this post I discussed the problem of a library author trying to insert middleware at a precise point in a consuming app's pipeline. You can use `IStartupFilter` to insert middleware at the _start_ of the pipeline, but it doesn't allow you to insert middleware at an arbitrary location, such as between the `UseRouting()` and `UseEndpoints()` calls.

As a potential solution to the problem, I gave a brief overview of the _MiddlewareAnalysis_ package that I've discussed previously, which inserts `AnalysisMiddleware` between every other middleware in your pipeline.

I then described a similar approach that can be used to insert our `ConditionalMiddleware` between each middleware in the pipeline. The `ConditionalMiddleware` can access the name of the next middleware in the pipeline, so that only the middleware instance placed just before the target middleware (`EndpointMiddleware`) executes its logic. The end result is somewhat convoluted, but achieves the desired goals!