はじめに
.NET Aspire を使って、Blazor Web App (BFF) から別サービスの API (Backend) へ YARP でリバースプロキシを張る際、404 Not Found や 502 Bad Gateway に悩まされることがあります。
一見正しいルーティングに見えても、実は HttpClient の作り方一つで名前解決が壊れてしまう という落とし穴についてまとめます。
発生していた現象
以下のような構成で、Blazor Server から Backend サービスへリクエストを転送しようとしました。
参考にしたのは下記ドキュメントです。
// Program.cs の一部
builder.Services.AddHttpForwarderWithServiceDiscovery();
// 自前で HttpClient (HttpMessageInvoker) を作成
var httpClient = new HttpMessageInvoker(new SocketsHttpHandler { /* カスタム設定 */ });
// YARP の MapForwarder で転送設定
app.MapForwarder("/api/{**any}", "https+http://backend", ForwarderRequestConfig.Empty, HttpTransformer.Default, httpClient);
結果: localhost/api/weatherforecast にアクセスすると、Backend サービスに届かず 404 Not Found が返ってくる。
原因:HttpClient が「サービス名」を理解できていない
原因は、MapForwarder に渡した 自作の httpClient にありました。
.NET Aspire では、https+http://backend という特殊な形式(スキーム)を使ってサービスを呼び出しますが、これを実際の IP アドレスや URL に変換するには、Service Discovery 用の MessageHandler が HttpClient のパイプラインに組み込まれている必要があります。
自前で new HttpMessageInvoker(new SocketsHttpHandler()) してしまうと、この「名前解決の仕組み」が一切含まれない "ただの HttpClient" になってしまいます。そのため、YARP は backend という名前のホストを DNS で見つけられず、転送に失敗して 404 を返していたのです。
解決策:YARP の標準機能に任せる
1. 最もシンプルな修正(推奨)
Aspireの場合はMicrosoft.Extensions.ServiceDiscovery.YarpパッケージのAddHttpForwarderWithServiceDiscovery()を使用してすることが必要でなおかつ、MapForwarder の引数で httpClient を指定しなければ、YARP は自動的に Service Discovery 対応済みの HttpClient を内部で使用してくれます。
builder.Services.AddHttpForwarderWithServiceDiscovery();
// 中略
// 引数を省略するだけで OK!
app.MapForwarder("/api/{**any}", "https+http://backend");
2. どうしても HttpClient をカスタマイズしたい場合
もしタイムアウト設定などで httpClient を自作したい場合は、IHttpClientFactory を経由して、Service Discovery が有効なクライアントを取得する必要があります。
※Microsoft.Extensions.ServiceDiscovery.Yarpパッケージが必要なのは一緒です。
// builder側で名前解決付きのクライアントを登録
builder.Services.AddHttpClient("MyProxyClient").AddServiceDiscovery();
// MapForwarder 内で factory から取得
app.MapForwarder("/api/{**any}", "https+http://backend", (context, forwarder, factory) => {
var client = factory.CreateClient("MyProxyClient");
return forwarder.SendAsync(context, "https+http://backend", client);
});
まとめ
- Aspire のサービス名 (https+http://...) は、標準の HttpClient では解釈できない。
- AddHttpForwarderWithServiceDiscovery を使うなら、MapForwarder の引数はできるだけシンプルに保つ(自前で httpClient を渡さない)。
- カスタマイズが必要な場合は、必ず AddServiceDiscovery() を通した HttpClient を使う。
この記事が皆様のコーディングライフの助けになれば幸いです。
参考