この記事では、IHttpClientFactory インターフェイスを使用して、依存関係の挿入 (DI)、ログ記録、構成など、さまざまな.NETの基礎を持つ HttpClient 型を作成する方法について説明します。
HttpClient 型は、2012 年にリリースされた .NET Framework 4.5 で導入されました。 つまり、使われるようになってしばらく経ちます。 は、HTTP 要求を行い、 によって識別される Web リソースからの HTTP 応答を処理するために使用されます。 HTTP プロトコルによって、すべてのインターネット トラフィックの大部分が構成されます。
ベスト プラクティスを推進する最新のアプリケーション開発原則に従うことにより、 は、カスタム構成で インスタンスを作成できるファクトリ抽象化として機能します。 IHttpClientFactory は、.NET Core 2.1 で導入されました。 一般的な HTTP ベースの.NET ワークロードでは、回復性と一時的な障害処理のサードパーティ ミドルウェアを簡単に利用できます。
警告
アプリで Cookie が必要な場合は、 を使用しないことをお勧めします。 インスタンスのプールを使用すると、 オブジェクトが共有されるようになります。 予期しない 共有により、アプリケーションの関連のない部分間で Cookie が漏洩する可能性があります。 さらに、 が期限切れになるとハンドラーがリサイクルされます。つまり、その に格納されているすべての Cookie が失われます。 クライアントを管理する別の方法については、「HTTP クライアントの使用に関するガイドライン」を参照してください。
重要
によって作成された インスタンスの有効期間の管理は、手動で作成されたインスタンスとは完全に異なります。 その方法は、 によって作成されたクライアントを使うか、 が設定されたクライアントを使うことです。 詳細については、「HttpClient の有効期間の管理」セクションと「HTTP クライアントの使用に関するガイドライン」を参照してください。
型
この記事で提供されているすべてのサンプル ソース コードで NuGet パッケージのインストールが必要です。 さらに、コード サンプルでは、無料の API からユーザー オブジェクトを取得するための HTTP 要求の使用方法を紹介しています。
拡張メソッドのいずれかを呼び出すと、 と関連サービスが に追加されます。 型には次のような利点があります。
- クラスを DI 対応型として公開します。
- 論理 インスタンスの名前付けと構成を一元化します。
- でのハンドラーのデリゲートにより、送信ミドルウェアの概念が体系化されます。
- でのハンドラーのデリゲートを利用するために、Polly ベースのミドルウェアに対する拡張メソッドが提供されます。
- 基になっている インスタンスのキャッシュと有効期間が管理されます。 自動管理により、 の有効期間を手動で管理するときの一般的なドメイン ネーム システム (DNS) の問題が発生しなくなります。
- ファクトリによって作成されたクライアントから送信されるすべての要求に対し、( によって) 構成可能なログ エクスペリエンスを追加します。
利用パターン
アプリで を使用するには複数の方法があります。
- 基本的な使用方法
- 名前付きクライアント
- 型指定されたクライアント
- 生成されたクライアント
どの方法が最善かは、アプリの要件によって異なります。
基本的な使用方法
を登録するには、 を呼び出します。
using Shared;
using BasicHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient();
builder.Services.AddTransient<TodoService>();
using IHost host = builder.Build();
サービスを利用する場合、 を使用してコンストラクター パラメーターとして を要求できます。 次のコードでは、 を使用して インスタンスを作成しています。
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;
namespace BasicHttp.Example;
public sealed class TodoService(
IHttpClientFactory httpClientFactory,
ILogger<TodoService> logger)
{
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
// Create the client
HttpClient client = httpClientFactory.CreateClient();
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo types
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"https://jsonplaceholder.typicode.com/todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
前の例のように を使用するのは、既存のアプリをリファクタリングするのに適した方法です。 の使用方法に対する影響はありません。 既存のアプリで のインスタンスが作成されている場所を、 の呼び出しに置き換えます。
名前付きクライアント
名前付きクライアントは、次の場合に適しています。
- アプリで、多くの異なる を使用する必要がある。
- 多くの インスタンスには異なる構成があります。
名前付き の構成は、 での登録時に指定できます:
using Shared;
using NamedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
string? httpClientName = builder.Configuration["TodoHttpClientName"];
ArgumentException.ThrowIfNullOrEmpty(httpClientName);
builder.Services.AddHttpClient(
httpClientName,
client =>
{
// Set the base address of the named client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
上記のコードでは、クライアントは次のもので構成されます。
- の下の構成からプルされた名前。
- ベース アドレス 。
- ヘッダー。
構成を使用して HTTP クライアント名を指定することができます。これは、追加時や作成時にクライアントに誤った名前を付けないようにするのに役立ちます。 この例では、HTTP クライアント名を構成するために appsettings.json ファイルが使用されています。
{
"TodoHttpClientName": "JsonPlaceholderApi"
}
この構成を拡張して、HTTP クライアントがどのように機能するかについての詳細を格納するのは簡単です。 詳細については、「.NET のConfiguration」を参照してください。
注
個別の登録済み名前付きクライアントの数は、リソースの枯渇につながる可能性があるため、無制限にしないでください。 たとえば、無制限の入力からクライアント名を派生させたりしないでください。
クライアントの作成
が呼び出されるたびに、次のことが行われます。
- の新しいインスタンスが作成されます。
- 構成アクションが呼び出されます。
名前付きクライアントを作成するには、その名前を に渡します。
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Shared;
namespace NamedHttp.Example;
public sealed class TodoService
{
private readonly IHttpClientFactory _httpClientFactory = null!;
private readonly IConfiguration _configuration = null!;
private readonly ILogger<TodoService> _logger = null!;
public TodoService(
IHttpClientFactory httpClientFactory,
IConfiguration configuration,
ILogger<TodoService> logger) =>
(_httpClientFactory, _configuration, _logger) =
(httpClientFactory, configuration, logger);
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
// Create the client
string? httpClientName = _configuration["TodoHttpClientName"];
HttpClient client = _httpClientFactory.CreateClient(httpClientName ?? "");
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo type
Todo[]? todos = await client.GetFromJsonAsync<Todo[]>(
$"todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
_logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
上記のコードでは、HTTP 要求でホスト名を指定する必要はありません。 クライアントに構成されているベース アドレスが使用されるため、コードではパスを渡すだけで済みます。
型指定されたクライアント
型指定されたクライアント:
- キーとして文字列を使用する必要なしに、名前付きクライアントと同じ機能を提供します。
- クライアントを使用するときに、IntelliSense とコンパイラのヘルプを提供します。
- 特定の を構成してそれと対話する 1 つの場所を提供します。 たとえば、単一の型指定されたクライアントは、次のために使用される場合があります。
- 単一のバックエンド エンドポイント用。
- エンドポイントを処理するすべてのロジックをカプセル化するため。
- DI に対応しており、アプリ内の必要な場所に挿入できます。
型指定されたクライアントは、そのコンストラクターで パラメーターを受け取ります。
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Shared;
namespace TypedHttp.Example;
public sealed class TodoService(
HttpClient httpClient,
ILogger<TodoService> logger)
{
public async Task<Todo[]> GetUserTodosAsync(int userId)
{
try
{
// Make HTTP GET request
// Parse JSON response deserialize into Todo type
Todo[]? todos = await httpClient.GetFromJsonAsync<Todo[]>(
$"todos?userId={userId}",
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return todos ?? [];
}
catch (Exception ex)
{
logger.LogError("Error getting something fun to say: {Error}", ex);
}
return [];
}
}
上のコードでは以下の操作が行われます。
- この構成は、型指定されたクライアントがサービス コレクションに追加されるときに設定されます。
- は、クラススコープの変数 (フィールド) として割り当てられ、公開された API と共に使用されます。
の機能を公開する API 固有のメソッドを作成できます。 たとえば、 メソッドでは、ユーザー固有の オブジェクトを取得するためのコードがカプセル化されます。
次のコードでは、 を呼び出して、型指定されたクライアント クラスを登録しています。
using Shared;
using TypedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHttpClient<TodoService>(
client =>
{
// Set the base address of the typed client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
型指定されたクライアントは、DI で一時的として登録されます。 上記のコードで、 は を一時的なサービスとして登録します。 この登録では、ファクトリ メソッドを使用して次のことを行います。
- のインスタンスを作成します。
- のインスタンスを作成し、 のインスタンスをそのコンストラクターに渡します。
重要
シングルトン サービスで型指定されたクライアントを使うと、危険な場合があります。 詳細については、シングルトン サービスでの型指定されたクライアントの回避に関するセクションを参照してください。
注
型指定されたクライアントを メソッドを使用して登録する場合、 型には、パラメータとして を許可するコンストラクタが必要です。 さらに、 型は DI コンテナを使用して個別に登録しないでください。登録すると、前登録が後の登録にとり上書きされるためです。
生成されたクライアント
は、Refit などのサードパーティ製ライブラリと組み合わせて使用できます。 再適合は、.NET用の REST ライブラリです。 これにより、インターフェイス メソッドをエンドポイントにマッピングする宣言型の REST API 定義が可能になります。 インターフェイスの実装は によって動的に生成され、 を使用して外部 HTTP の呼び出しを行います。
次の 型があるとします:
namespace Shared;
public record class Todo(
int UserId,
int Id,
string Title,
bool Completed);
次の例は、 NuGet パッケージに依存しており、単純なインターフェイスです。
using Refit;
using Shared;
namespace GeneratedHttp.Example;
public interface ITodoService
{
[Get("/todos?userId={userId}")]
Task<Todo[]> GetUserTodosAsync(int userId);
}
上記の C# インターフェイスでは、次のことを行います。
- インスタンスを返す という名前のメソッドを定義します。
- 外部 API に対するパスとクエリ文字列を使用して、 属性を宣言します。
型指定されたクライアントを追加し、Refit を使用して実装を生成できます。
using GeneratedHttp.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Refit;
using Shared;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddRefitClient<ITodoService>()
.ConfigureHttpClient(client =>
{
// Set the base address of the typed client.
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
// Add a user-agent default request header.
client.DefaultRequestHeaders.UserAgent.ParseAdd("dotnet-docs");
});
定義されたインターフェイスは、DI および Refit によって提供される実装で、必要に応じて使用できます。
POST、PUT、DELETE の要求を行う
前の例では、すべての HTTP 要求で HTTP 動詞が使用されています。 では、次のような他の HTTP 動詞もサポートされています。
POSTPUTDELETEPATCH
サポートされている HTTP 動詞の一覧については、「」を参照してください。 HTTP 要求の作成の詳細については、「HttpClient を使用して要求を送信する」 を参照してください。
次の例は、HTTP 要求の方法を示しています。
public async Task CreateItemAsync(Item item)
{
using StringContent json = new(
JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
Encoding.UTF8,
MediaTypeNames.Application.Json);
using HttpResponseMessage httpResponse =
await httpClient.PostAsync("/api/items", json);
httpResponse.EnsureSuccessStatusCode();
}
上記のコードの メソッドは:
- を使用して パラメーターを JSON にシリアル化します。 これは、 のインスタンスを使用して、シリアル化プロセスを構成します。
- HTTP 要求の本文で送信するためにシリアル化された JSON をパッケージ化する のインスタンスを作成します。
- を呼び出して、指定した URL に JSON の内容を送信します。 これは HttpClient.BaseAddress に追加される相対 URL です。
- 応答状態コードが成功を示していない場合に、 を呼び出して例外をスローします。
は、他の種類のコンテンツもサポートしています。 たとえば、 や です。 サポートされているコンテンツの一覧については、「」を参照してください。
HTTP 要求の例を次に示します。
public async Task UpdateItemAsync(Item item)
{
using StringContent json = new(
JsonSerializer.Serialize(item, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
Encoding.UTF8,
MediaTypeNames.Application.Json);
using HttpResponseMessage httpResponse =
await httpClient.PutAsync($"/api/items/{item.Id}", json);
httpResponse.EnsureSuccessStatusCode();
}
上記のコードは、 の例によく似ています。 メソッドは ではなく を呼び出します。
HTTP 要求の例を次に示します。
public async Task DeleteItemAsync(Guid id)
{
using HttpResponseMessage httpResponse =
await httpClient.DeleteAsync($"/api/items/{id}");
httpResponse.EnsureSuccessStatusCode();
}
上記のコードの メソッドは を呼び出します。 HTTP 要求には通常、本文が含まれていないため、 メソッドは、 のインスタンスを受け入れるオーバーロードを提供しません。
でのさまざまな HTTP 動詞の使用の詳細については、「」を参照してください。
有効期間管理
で を呼び出すたびに、 の新しいインスタンスが返されます。 クライアント名ごとに 1 つの インスタンスが作成されます。 ファクトリによって インスタンスの有効期間が管理されます。
は、リソースの消費量を減らすため、ファクトリによって作成された のインスタンスをキャッシュします。 新しい インスタンスを作成するときに、キャッシュの インスタンスの有効期間が切れていない場合はそれを再利用する場合があります。
通常、各ハンドラーでは基になる HTTP 接続プールが独自に管理されるため、ハンドラーはキャッシュすることが望まれます。 必要以上のハンドラーを作成すると、ソケット不足と接続の遅延が発生するおそれがあります。 また、一部のハンドラーは接続を無期限に開いており、DNS の変更にハンドラーが対応できないことがあります。
ハンドラーの既定の有効期間は 2 分です。 既定値をオーバーライドするには、にを登録するときに、でクライアントごとにを呼び出します。
services.AddHttpClient("Named.Client")
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
重要
instances created by によって作成される インスタンスは、短い有効期間であることが想定されます。
ハンドラーが DNS の変更に確実に対応するためには、有効期間の有効期限が切れたときの のリサイクルと再作成が に不可欠です。 は作成時に特定のハンドラー インスタンスに関連付けられるため、新しい インスタンスを適切なタイミングで要求し、クライアントが更新されたハンドラーを確実に取得できるようにする必要があります。
このような インスタンスを破棄してもソケットは枯渇しません。破棄しても は破棄ためです。 は インスタンスの作成に使用されたリソース (具体的には インスタンス) を追跡し、破棄します。それらの有効期間はすぐに期限切れになり、それらを使う がなくなるからです。
1 つの インスタンスを長期間存続させることは、 のとして使用できる一般的なパターンですが、このパターンには のような追加のセットアップが必要です。 を指定したクライアントを使うか、 によって作成されたクライアントを使うことができます。 アプリで使用する戦略の詳細については、「HTTP クライアントの使用に関するガイドライン」を参照してください。
を構成する
クライアントによって使用される内部 の構成を制御することが必要な場合があります。
名前付きクライアントまたは型指定されたクライアントを追加すると、 が返されます。 拡張メソッドは、で呼び出してデリゲートで渡すことができます。 デリゲートは、そのクライアントによって使用されるプライマリ の作成と構成に使用されます。
.ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
AllowAutoRedirect = false,
UseDefaultCredentials = true
};
});
を構成すると、ハンドラーのさまざまな他のプロパティの中から インスタンスのプロキシを指定できます。 詳細については、クライアントごとのプロキシに関する記事を参照してください。
追加の設定
を制御するための追加の構成オプションがいくつかあります。
| メソッド | 説明 |
|---|---|
| AddHttpMessageHandler | 名前付き のメッセージ ハンドラーをさらに追加します。 |
| AddTypedClient | と、 に関連付けられている名前付き との間のバインディングを構成します。 |
| ConfigureHttpClient | 名前付き の構成に使用されるデリゲートを追加します。 |
| ConfigurePrimaryHttpMessageHandler | 名前付き に対して、依存関係挿入コンテナーからプライマリ を構成します。 |
| RedactLoggedHeaders | ログ記録する前に値を編集する必要がある HTTP ヘッダー名のコレクションを設定します。 |
| SetHandlerLifetime | を再利用できる時間を設定します。 名前付きの各クライアントには、独自の構成済みハンドラーの有効期間の値を設定できます。 |
| UseSocketsHttpHandler | 名前付き のプライマリ ハンドラとして使用されるように依存性の注入コンテナから新しいまたは前に追加した インスタンスを構成します。 (.NET 5+ のみ) |
とを一緒に使用する
SocketsHttpHandler
HttpMessageHandler の実装が .NET Core 2.1 に追加されました。これにより、PooledConnectionLifetimeを構成できます。 この設定は、ハンドラーが DNS の変更に対応できるようにするために使用されます。そのため、 の使用は の使用の代替手段と見なされます。 詳細については、「HttpClient の使用に関するガイドライン」を参照してください。
ただし、 と を一緒に使用して、構成性を向上させることができます。 これらの API の両方を使うことで、低レベル (たとえば、動的な証明書の選択に を使う) と高レベル (たとえば、DI 統合や複数のクライアント構成を活用する) の両方で構成可能性の利点を得ることができます。
両方の API を使うには:
-
SocketsHttpHandlerをPrimaryHandlerもしくは UseSocketsHttpHandler (.NET 5+ のみ) として ConfigurePrimaryHttpMessageHandler 経由で指定します。 - DNS の更新が予想される間隔 (たとえば、拡張メソッドで以前に設定した値) に基づいて、を設定します。
- (オプション) が接続ポールとリサイクルを処理するため、 レベルのハンドラ リサイクルは不要になりました。 を に設定して、サイズ変更を無効にすることもできます。
services.AddHttpClient(name)
.UseSocketsHttpHandler((handler, _) =>
handler.PooledConnectionLifetime = TimeSpan.FromMinutes(2)) // Recreate connection every 2 minutes
.SetHandlerLifetime(Timeout.InfiniteTimeSpan); // Disable rotation, as it is handled by PooledConnectionLifetime
上の例では、既定の 値に合わせて、図解の目的で 2 分を任意に選択しました。 DNS またはその他のネットワーク変更の予想される頻度に基づいて値を選択する必要があります。 詳細については、 ガイドラインのセクションと API ドキュメントの「備考」セクションを参照してください。
シングルトン サービスで型指定されたクライアントを使う
"名前付きクライアント" の方法を使用する場合、 はサービスに挿入され、 インスタンスは が必要になるたびに を呼び出すことによって作成されます。
ただし、"型指定されたクライアント" の方法では、型指定されたクライアントは、通常はサービスに挿入される一時的なオブジェクトになります。 これにより問題が発生するおそれがあります。型指定されたクライアントをシングルトン サービスに挿入できるためです。
重要
型指定されたクライアントは、 によって作成された インスタンスと同じ意味で、であることが想定されます (詳細については、「 の有効期間の管理」を参照)。 型指定されたクライアント インスタンスが作成されるとすぐに、 ではそれを制御できなくなります。 型指定されたクライアント インスタンスがシングルトンで取得された場合、DNS の変更に対応できなくなり、 の目的の 1 つが失われるおそれがあります。
シングルトン サービスで インスタンスを使う必要がある場合は、次のオプションを検討してください。
- 代わりに "名前付きクライアント" の方法を使い、シングルトン サービスに を挿入し、必要に応じて インスタンスを再作成します。
- "型指定されたクライアント" の方法が必要な場合は、構成済みの と共にプライマリ ハンドラーとして を使います。 と を使う方法について詳しくは、セクション「SocketsHttpHandler と共に IHttpClientFactory を使用する」 を参照してください。
のメッセージ ハンドラー スコープ
は、各 インスタンスごとに個別の DI スコープを作成します。 これらの DI スコープは、アプリケーションの DI スコープ (例えば、ASP.NET の受信要求スコープやユーザーが作成した手動 DI スコープ)とは別のものであるため、スコープ付きのサービス インスタンスは共有されません。 メッセージ ハンドラー スコープはハンドラーの有効期間に関連付けられており、アプリケーション スコープを上回る可能性があります。これは、たとえば、複数の受信要求間で同じスコープの依存関係が挿入された同じ インスタンスを再利用する可能性があります。
2 つのアプリケーション DI スコープと個別のメッセージ ハンドラー スコープを示す図
インスタンス内で ( のデータなどの) ことと、機密情報を漏洩しないように注意してスコープ付き依存関係を使うことを強くお勧めします。
メッセージ ハンドラーからアプリ DI スコープにアクセスする必要がある場合は、認証の例として、スコープ対応ロジックを別の一時的な にカプセル化し、 キャッシュから インスタンスの周囲にラップします。 登録済みの のハンドラー呼び出し にアクセスします。 その場合は、構築されたハンドラーを使用して自分で インスタンスを作成します。
別の一時的なメッセージハンドラと IH ttpMessageHandlerFactory を介してアプリ DI スコープにアクセスすることを示すダイアグラム
次の例は、スコープ対応の を使用して を作成する方法を示しています:
if (scopeAwareHandlerType != null)
{
if (!typeof(DelegatingHandler).IsAssignableFrom(scopeAwareHandlerType))
{
throw new ArgumentException($"""
Scope aware HttpHandler {scopeAwareHandlerType.Name} should
be assignable to DelegatingHandler
""");
}
// Create top-most delegating handler with scoped dependencies
scopeAwareHandler = (DelegatingHandler)_scopeServiceProvider.GetRequiredService(scopeAwareHandlerType); // should be transient
if (scopeAwareHandler.InnerHandler != null)
{
throw new ArgumentException($"""
Inner handler of a delegating handler {scopeAwareHandlerType.Name} should be null.
Scope aware HttpHandler should be registered as Transient.
""");
}
}
// Get or create HttpMessageHandler from HttpClientFactory
HttpMessageHandler handler = _httpMessageHandlerFactory.CreateHandler(name);
if (scopeAwareHandler != null)
{
scopeAwareHandler.InnerHandler = handler;
handler = scopeAwareHandler;
}
HttpClient client = new(handler);
さらに回避策として、スコープ対応 を登録し、現在のアプリ スコープにアクセスできる一時的なサービスによる既定 の登録をオーバーライドするための拡張メソッドを使用できます:
public static IHttpClientBuilder AddScopeAwareHttpHandler<THandler>(
this IHttpClientBuilder builder) where THandler : DelegatingHandler
{
builder.Services.TryAddTransient<THandler>();
if (!builder.Services.Any(sd => sd.ImplementationType == typeof(ScopeAwareHttpClientFactory)))
{
// Override default IHttpClientFactory registration
builder.Services.AddTransient<IHttpClientFactory, ScopeAwareHttpClientFactory>();
}
builder.Services.Configure<ScopeAwareHttpClientFactoryOptions>(
builder.Name, options => options.HttpHandlerType = typeof(THandler));
return builder;
}
詳細については、完全な例を参照してください。
"出荷時の既定" プライマリ ハンドラーに依存しないようにする
このセクションでは、 "factory-default" プライマリ ハンドラー という用語は、既定の 実装 (つまり、既定の 実装) が割り当てるプライマリ ハンドラーを指します (何 らかの方法で構成されていない 場合)。
注
"出荷時の既定" プライマリ ハンドラーは実装の 詳細 であり、変更される可能性があります。 "工場出荷時の既定値" として使用されている特定の実装 (たとえば、 ) に応じて回避します。
特にクラス ライブラリで作業している場合は、プライマリ ハンドラーの特定の種類を知る必要がある場合があります。 エンド ユーザーの構成を保持しながら、たとえば、、、などの固有のプロパティを更新できます。 プライマリ ハンドラーを にキャストすることも考えられますが、これは、 が "工場出荷時の既定" のプライマリ ハンドラーとして使用されている場合にのみ動作した方法です。 しかし、実装の詳細に応じたコードのように、このような回避策は 脆弱 であり、壊れる可能性があります。
"出荷時の既定" プライマリ ハンドラーに依存する代わりに、 を使用して "アプリ レベル" の既定のプライマリ ハンドラー インスタンスを設定できます。
// Contract with the end-user: Only HttpClientHandler is supported.
// --- "Pre-configure" stage ---
// The default is fixed as HttpClientHandler to avoid depending on the "factory-default"
// Primary Handler.
services.ConfigureHttpClientDefaults(b =>
b.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false }));
// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...
// --- "Post-configure" stage ---
// The code can rely on the contract, and cast to HttpClientHandler only.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
if (handler is not HttpClientHandler h)
{
throw new InvalidOperationException("Only HttpClientHandler is supported");
}
h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
//X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
});
または、プライマリ ハンドラーの種類を確認し、既知のサポートの種類 (多くの場合、 と ) でのみクライアント証明書などの詳細を構成することもできます。
// --- "End-user" stage ---
// IHttpClientBuilder builder = services.AddHttpClient("test", /* ... */);
// ...
// --- "Post-configure" stage ---
// No contract is in place. Trying to configure main handler types supporting client
// certs, logging and skipping otherwise.
builder.ConfigurePrimaryHttpMessageHandler((handler, provider) =>
{
if (handler is HttpClientHandler h)
{
h.ClientCertificates.Add(GetClientCert(provider, builder.Name));
}
else if (handler is SocketsHttpHandler s)
{
s.SslOptions ??= new System.Net.Security.SslClientAuthenticationOptions();
s.SslOptions.ClientCertificates ??= new X509CertificateCollection();
s.SslOptions.ClientCertificates!.Add(GetClientCert(provider, builder.Name));
}
else
{
// Log warning
}
//X509Certificate2 GetClientCert(IServiceProvider p, string name) { ... }
});
関連項目
- 一般的な使用に関する問題
- .NET における依存性注入
- .NETでのログ記録
- .NETの構成
- IHttpClientFactory
- IHttpMessageHandlerFactory
- HttpClient
- HttpClient を使用して HTTP 要求を行う
- 指数バックオフを使用して HTTP 再試行を実装する
.NET