この記事では、この API のリファレンス ドキュメントに補足的な解説を提供します。
HttpClient クラス インスタンスは、HTTP 要求を送信するセッションとして機能します。 HttpClient インスタンスは、そのインスタンスによって実行されるすべての要求に適用される設定のコレクションです。 さらに、すべての HttpClient インスタンスは独自の接続プールを使用し、他の HttpClient インスタンスによって実行される要求から要求を分離します。
インスタンス化
HttpClient は、1 回インスタンス化され、アプリケーションの有効期間中に再利用されることを目的としています。 .NET Core と .NET 5 以降では、HttpClient はハンドラー インスタンス内の接続をプールし、複数の要求にわたって接続を再利用します。 すべての要求に対して HttpClient クラスをインスタンス化すると、大量の負荷で使用可能なソケットの数が使い果たされます。 この枯渇により、 SocketException エラーが発生します。
HttpClientHandler (または .NET Core 2.1 以降のSocketsHttpHandler) などの "ハンドラー" をコンストラクターの一部として渡すことで、追加のオプションを構成できます。 要求が送信された後にハンドラーの接続プロパティを変更することはできません。そのため、新しい HttpClient インスタンスを作成する理由の 1 つは、接続プロパティを変更する必要がある場合です。 異なる要求で異なる設定が必要な場合は、アプリケーションに複数の HttpClient インスタンスがあり、各インスタンスが適切に構成され、関連するクライアントで要求が発行される可能性もあります。
HttpClient は、接続の作成時にのみ DNS エントリを解決します。 DNS サーバーによって指定されている有効期限 (TTL) の期間は追跡されません。 DNS エントリが定期的に変更され、一部のコンテナー シナリオで発生する可能性がある場合、クライアントはこれらの更新プログラムを尊重しません。 この問題を解決するには、接続が置き換えられるときに DNS 参照が必要になるように、 SocketsHttpHandler.PooledConnectionLifetime プロパティを設定することで、接続の有効期間を制限できます。
public class GoodController : ApiController
{
private static readonly HttpClient httpClient;
static GoodController()
{
var socketsHandler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(2)
};
httpClient = new HttpClient(socketsHandler);
}
}
1 つの HttpClient インスタンスのみを作成する代わりに、 IHttpClientFactory を使用して HttpClient インスタンスを管理することもできます。 詳細については、「HttpClient の使用に関するガイドライン」を参照してください。
導出
HttpClientは、より具体的な HTTP クライアントの基底クラスとしても機能します。 たとえば、Facebook Web サービスに固有の追加メソッド (たとえば、 GetFriends
メソッド) を提供する FacebookHttpClient です。 派生クラスは、クラスの仮想メソッドをオーバーライドしないでください。 代わりに、 HttpMessageHandler を受け入れるコンストラクター オーバーロードを使用して、要求前または要求後の処理を構成します。
トランスポート
HttpClientは、実行される各プラットフォームで使用できる下位レベルの機能をラップする高度な API です。
各プラットフォームで、 HttpClient は使用可能な最適なトランスポートの使用を試みます。
ホスト/ランタイム | バックエンド の |
---|---|
Windows/.NET Framework | HttpWebRequest |
Windows/Mono | HttpWebRequest |
Windows/UWP | Windows ネイティブ WinHttpHandler (HTTP 2.0 対応) |
Windows/.NET Core 1.0-2.0 | Windows ネイティブ WinHttpHandler (HTTP 2.0 対応) |
macOS/Mono | HttpWebRequest |
macOS/.NET Core 1.0-2.0 |
libcurl -based HTTP トランスポート (HTTP 2.0 対応) |
Linux/Mono | HttpWebRequest |
Linux/.NET Core 1.0-2.0 |
libcurl -based HTTP トランスポート (HTTP 2.0 対応) |
.NET Core 2.1 以降 | System.Net.Http.SocketsHttpHandler |
ユーザーは、HttpMessageHandlerを受け取るHttpClientコンストラクターを呼び出すことによって、HttpClientの特定のトランスポートを構成することもできます。
.NET Framework & Mono
既定では、.NET Framework と Mono では、 HttpWebRequest を使用してサーバーに要求を送信します。 この動作は、 HttpMessageHandler パラメーターを使用してコンストラクター オーバーロードの 1 つで別のハンドラーを指定することで変更できます。 認証やキャッシュなどの機能が必要な場合は、 WebRequestHandler を使用して設定を構成し、インスタンスをコンストラクターに渡すことができます。 返されたハンドラーは、 HttpMessageHandler パラメーターを持つコンストラクター オーバーロードに渡すことができます。
.NET コア
.NET Core 2.1 以降では、HttpClientHandlerではなく System.Net.Http.SocketsHttpHandler クラスによって、HttpClientなどの上位レベルの HTTP ネットワーク クラスで使用される実装が提供されます。 SocketsHttpHandlerの使用には、いくつかの利点があります。
- 以前の実装と比較して、パフォーマンスが大幅に向上しています。
- プラットフォームの依存関係が排除され、デプロイとサービスが簡略化されます。 たとえば、
libcurl
は macOS 用の .NET Core と Linux 用の .NET Core への依存関係ではなくなりました。 - すべての .NET プラットフォームで一貫した動作。
この変更が望ましくない場合、Windows では、NuGet パッケージを参照して HttpClient のコンストラクターに手動で渡すことで、WinHttpHandlerを引き続き使用できます。
ランタイム構成オプションを使用して動作を構成する
HttpClientの動作の特定の側面は、ランタイム構成オプションを使用してカスタマイズできます。 ただし、これらのスイッチの動作は.NET バージョンによって異なります。 たとえば、.NET Core 2.1 から 3.1 では、 SocketsHttpHandler を既定で使用するかどうかを構成できますが、そのオプションは .NET 5 以降では使用できなくなります。
接続のプール
HttpClient は可能な限り HTTP 接続をプールし、複数の要求に使用します。 接続ハンドシェイクは 1 回だけ実行されるため、特に HTTPS 要求の場合、パフォーマンスに大きなメリットがあります。
接続プールのプロパティは、HttpClientHandlerで構成することも、MaxConnectionsPerServer、PooledConnectionIdleTimeout、PooledConnectionLifetimeなど、構築中に渡SocketsHttpHandlerすることもできます。
HttpClient インスタンスを破棄すると、開いている接続が閉じられ、保留中の要求が取り消されます。
注
HTTP/1.1 要求を同じサーバーに同時に送信する場合は、新しい接続を作成できます。
HttpClient
インスタンスを再利用する場合でも、要求のレートが高い場合やファイアウォールの制限がある場合は、既定の TCP クリーンアップ タイマーのために使用可能なソケットが使い果たされる可能性があります。 同時接続の数を制限するには、 MaxConnectionsPerServer
プロパティを設定します。 既定では、同時 HTTP/1.1 接続の数は無制限です。
バッファリングと要求の有効期間
既定では、HttpClient メソッド ( GetStreamAsyncを除く) はサーバーからの応答をバッファー処理し、非同期結果を返す前にすべての応答本文をメモリに読み込みます。 これらの要求は、次のいずれかが発生するまで続行されます。
- Task<TResult>は成功し、結果を返します。
- Timeoutに達すると、Task<TResult>は取り消されます。
- 一部のメソッド オーバーロードに渡 CancellationToken が発生します。
- CancelPendingRequests() が呼び出されます
- HttpClient は破棄されます。
一部のメソッド オーバーロードで使用できる HttpCompletionOption パラメーターを使用して、要求ごとにバッファリング動作を変更できます。 この引数を使用して、応答ヘッダーのみを読み取った後、または応答の内容を読み取ってバッファー処理した後に、 Task<TResult> を完了と見なす必要があるかどうかを指定できます。
System.Net.Http名前空間でHttpClientおよび関連クラスを使用するアプリが大量のデータ (50 メガバイト以上) をダウンロードする場合、アプリはそれらのダウンロードをストリーミングし、既定のバッファリングを使用しないようにする必要があります。 既定のバッファリングを使用すると、クライアントのメモリ使用量が非常に大きくなり、パフォーマンスが大幅に低下する可能性があります。
スレッドの安全性
次のメソッドはスレッド セーフです。
- CancelPendingRequests
- DeleteAsync
- GetAsync
- GetByteArrayAsync
- GetStreamAsync
- GetStringAsync
- PostAsync
- PutAsync
- SendAsync
プロキシ
既定では、HttpClient は、プラットフォームに応じて、環境変数またはユーザー/システム設定からプロキシ構成を読み取ります。 この動作を変更するには、優先順位に従って WebProxy または IWebProxy を渡します。
- HttpClient の構築中に渡された HttpClientHandler の Proxy プロパティ
- DefaultProxy静的プロパティ (すべてのインスタンスに影響します)
UseProxyを使用してプロキシを無効にすることができます。 Windows ユーザーの既定の構成では、ネットワーク検出を使用してプロキシを検出しようとしますが、これは低速になる可能性があります。 プロキシが必要ないことがわかっている高スループット アプリケーションの場合は、プロキシを無効にする必要があります。
プロキシ設定 ( Credentialsなど) は、HttpClient を使用して最初の要求が行われる前にのみ変更する必要があります。 HttpClient を初めて使用した後に行われた変更は、後続の要求には反映されない場合があります。
タイムアウト
Timeoutを使用して、HttpClient インスタンスからのすべての HTTP 要求の既定のタイムアウトを設定できます。 タイムアウトは、要求/応答が開始される xxxAsync メソッドにのみ適用されます。 タイムアウトに達すると、その要求の Task<TResult> は取り消されます。
HttpClient オブジェクトを構築するときに SocketsHttpHandler インスタンスを渡す場合は、いくつかの追加のタイムアウトを設定できます。
プロパティ | 説明 |
---|---|
ConnectTimeout | 要求で新しい TCP 接続を作成する必要がある場合に使用されるタイムアウトを指定します。 タイムアウトが発生した場合、要求 Task<TResult> は取り消されます。 |
PooledConnectionLifetime | 接続プール内の各接続に使用するタイムアウトを指定します。 接続がアイドル状態の場合、接続はすぐに閉じられます。それ以外の場合、接続は現在の要求の最後に閉じられます。 |
PooledConnectionIdleTimeout | 接続プール内の接続がこの長い間アイドル状態の場合、接続は閉じられます。 |
Expect100ContinueTimeout | 要求に "Expect: 100-continue" ヘッダーがある場合、タイムアウトになるまで、または "100-continue" 応答が受信されるまで、コンテンツの送信が遅れます。 |
HttpClient は、接続の作成時にのみ DNS エントリを解決します。 DNS サーバーによって指定されている有効期限 (TTL) の期間は追跡されません。 一部のコンテナー シナリオで発生する可能性がある DNS エントリが定期的に変更される場合は、 PooledConnectionLifetime を使用して接続の有効期間を制限し、接続を置き換えるときに DNS 参照が必要になるようにすることができます。
.NET