自前でリバースプロクシを実装するのに使っていた ProxyKit が開発終了し、他のライブラリに移行しなければいけなくなった。筆頭候補は、1.0.0 に到達した Microsoft 製の YARP。
そもそも、IIS や Nginx を使わず、ProxyKit を使って自前でリバースプロクシを実装していたのは、HTTP リクエストヘッダーとデータベースを参照して、動的に転送先を決定する要件があったからだ。
YARP で動的に転送先を決定できるか調べてみた。結論を言えば、IHttpForwarder を使うことで実現可能。
using System; using System.Net; using System.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Yarp.ReverseProxy.Forwarder; namespace HelloYarp { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } public class Startup { private const string WebApiV1Url = "http://localhost:5001"; private const string WebApiV2Url = "http://localhost:5002"; public IConfiguration Configuration { get; } public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddHttpForwarder(); } public void Configure(IApplicationBuilder app, IHttpForwarder forwarder) { var httpClient = new HttpMessageInvoker(new SocketsHttpHandler { UseProxy = false, AllowAutoRedirect = false, AutomaticDecompression = DecompressionMethods.None, UseCookies = false, }); var transformer = HttpTransformer.Default; var requestConfig = new ForwarderRequestConfig { ActivityTimeout = TimeSpan.FromMinutes(4), }; app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.Map("/{**catch-all}", async context => { var destinetion = WebApiV2Url; if(context.Request.Headers.TryGetValue("X-API-Version", out var versions)) { if (versions[0] == "v1") { destinetion = WebApiV1Url; } } var error = await forwarder.SendAsync( context: context, destinationPrefix: destinetion, httpClient: httpClient, requestConfig: requestConfig, transformer: transformer); if (error != ForwarderError.None) { var errorFeature = context.Features.Get<IForwarderErrorFeature>(); var exception = errorFeature.Exception; var logger = context.RequestServices.GetService<ILogger>(); logger.LogError( exception, $"エラー発生"); } }); }); } } }
本番では JWT を取得し、JWT が持つクレームをもとにデータベースにアクセスし、転送先を決定する。このサンプルでは簡略化。
動的に転送先が決定できることがわかったので、ProxyKit からの移行はスムーズにできた。