Matthias
Matthias

Reputation: 3573

Middleware that calls other middlewares

I have some piece of ASP.NET Core code that I want to extract into a custom middleware. Specifically, the following logic should be implemented: when a certain path mapPath is requested, proxy the request to another host, identified by proxyUrl.

The following code in Startup.cs does the trick:

var proxyUri = new Uri(proxyUrl);
builder.Map(
    mapPath,
    appMapped =>
    {
        appMapped.RunProxy(
            new ProxyOptions
                {
                    Scheme = proxyUri.Scheme,
                    Host = proxyUri.Host,
                    Port = proxyUri.Port.ToString()
                });
    }
);

Well, it uses app.Map() to branch and then the Proxy middleware to proxy the request.

(How) Is it possible to extract this logic to a custom and resusable middleware? Or am I not able to use a "real" middleware here? What I can do is of course write an extension method, e.g. app.UseMapProxy() and put the logic 1:1 in there, but I just wondered if I can do it with a "real" middleware class as well.

Upvotes: 2

Views: 725

Answers (2)

tsemer
tsemer

Reputation: 3178

For me, while the accepted answer made sense, it wasn't satisfying because it didn't allow for proper encapsulation. app.UseMapProxy() will naturally need parameters for proxyUrl and mapPath, which I prefer the caller not to be aware of. And in my case there is much more logic and a few more dependencies - the logic would have to live in extension methods, and because it's all static there is no DI, which means the dependencies will all have to be specified by the caller. Lastly, no DI means limited testing capacity.

Some searching on MS docs and other resources didn't come up with a recommended pattern. So I sat to the drawing board and came up with the following pattern:

  1. A class MyMiddlewareWrapper or just MyThing:
    • Employ DI and encapsulates logic
    • Expose a method Use(IApplicationBuilder)
    • Add tests if needed
  2. A class MyThingExtensions to stay as close to conventions as possible, and encapsulate MyThing entirely:
    • Expose AddMything(this IServiceCollection)
    • Expose UseMyThing(this IApplicationBuilder),
      which uses builder.ApplicationServices.GetRequiredService<MyThing>()
      (generally an anti-pattern, but alternative of leaking dependencies seems worse)

Here's the code as implemented for your example:

public class MyProxyOptions {
  public string ProxyUrl { get; set; }
  public string PathRequiringProxying { get; set; }
}

public class MyProxy {
  private readonly MyProxyOptions _proxyOptions;

  public MyProxy(IOptions<MyProxyOptions> proxyOptions) {
    _proxyOptions = proxyOptions.Value;
  }

  public IApplicationBuilder Use(IApplicationBuilder builder) {
    var proxyUri = new Uri(_proxyOptions.ProxyUrl);

    return builder.Map(_proxyOptions.PathRequiringProxying,
      builderBranch => builderBranch.RunProxy(new ProxyOptions {
        Scheme = proxyUri.Scheme,
        Host = proxyUri.Host,
        Port = proxyUri.Port.ToString()
      }));
  }
}

public static class MyProxyExtensions {
  public static IServiceCollection AddMyProxy(this IServiceCollection services) {
    return services.AddSingleton<MyProxy>();
  }

  public static IApplicationBuilder UseMyProxy(this IApplicationBuilder builder) {
    var myProxy = builder.ApplicationServices.GetRequiredService<MyProxy>();
    return myProxy.Use(builder);
  }
}

Upvotes: 0

Tratcher
Tratcher

Reputation: 6094

This kind of setup is best encapsulated in an IApplicationBuilder extension method. You're not adding any per-request functionality beyond the existing components, just hooking them up together.

Upvotes: 1

Related Questions