Reputation: 3573
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
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:
MyMiddlewareWrapper
or just MyThing
:
Use(IApplicationBuilder)
MyThingExtensions
to stay as close to conventions as possible, and encapsulate MyThing
entirely:
AddMything(this IServiceCollection)
UseMyThing(this IApplicationBuilder)
,builder.ApplicationServices.GetRequiredService<MyThing>()
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
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