次の方法で共有


HttpWebRequest から HttpClient への移行ガイド

この記事は、HttpWebRequestServicePointServicePointManager から HttpClient に移行するプロセスを開発者に案内することを目的としています。 古い API の陳腐化に対応し、パフォーマンスの向上/リソース管理の向上/よりモダンで柔軟な API 設計といった HttpClient のさまざまなメリットを享受するうえで移行が必要となります。 このドキュメントで説明されている手順に従うことで、開発者はコードベースをスムーズに移行し、HttpClient に用意されている機能を最大限に活用できるようになります。

警告

HttpWebRequestServicePointServicePointManager から HttpClient への移行は、パフォーマンス向上のために「実施しておいた方が良い」というだけではありません。 .NET (Core) への移行後は、既存の WebRequest ロジックのパフォーマンスが大幅に低下する可能性があることを理解することが重要です。 これは、WebRequest が最小限の互換性レイヤーとして維持されるためです。つまり、さまざまなケースで接続を再利用するなど、多くの最適化が欠けています。 そのため、アプリケーションのパフォーマンスとリソース管理で最新の標準に準拠するためには、HttpClient への移行が不可欠です。

HttpWebRequest から HttpClient に移行する

まずはいくつか例を紹介します。

HttpWebRequest を使用するシンプルな GET リクエスト

コードは次のようになります。

HttpWebRequest request = WebRequest.CreateHttp(uri);
using WebResponse response = await request.GetResponseAsync();

HttpClient を使用するシンプルな GET リクエスト

コードは次のようになります。

HttpClient client = new();
using HttpResponseMessage message = await client.GetAsync(uri);

HttpWebRequest を使用するシンプルな POST リクエスト

コードは次のようになります。

HttpWebRequest request = WebRequest.CreateHttp(uri);
request.Method = "POST";
request.ContentType = "text/plain";
await using Stream stream = await request.GetRequestStreamAsync();
await stream.WriteAsync("Hello World!"u8.ToArray());
using WebResponse response = await request.GetResponseAsync();

HttpClient を使用するシンプルな POST リクエスト

コードは次のようになります。

HttpClient client = new();
using HttpResponseMessage responseMessage = await client.PostAsync(uri, new StringContent("Hello World!"));

HttpWebRequest から HttpClient、SocketsHttpHandler 移行ガイド

HttpWebRequest 古い API 新しい API メモ
Accept Accept 例: 要求ヘッダーを設定する
Address RequestUri 例: リダイレクトされた URI を取得する
AllowAutoRedirect AllowAutoRedirect 例: SocketsHttpHandler プロパティの設定
AllowReadStreamBuffering 同等の API なし バッファリング プロパティの使用
AllowWriteStreamBuffering 同等の API なし バッファリング プロパティの使用
AuthenticationLevel 同等の API なし 例: 相互認証の有効化
AutomaticDecompression AutomaticDecompression 例: SocketsHttpHandler プロパティの設定
CachePolicy 同等の API なし 例: CachePolicy ヘッダーを適用する
ClientCertificates ClientCertificates= HttpClient における証明書関連プロパティの使用
Connection Connection 例: 要求ヘッダーを設定する
ConnectionGroupName 同等の API なし 回避策なし
ContentLength ContentLength 例: コンテンツ ヘッダーを設定する
ContentType ContentType 例: コンテンツ ヘッダーを設定する
ContinueDelegate 同等の API なし 回避策はありません。
ContinueTimeout Expect100ContinueTimeout 例: SocketsHttpHandler プロパティの設定
CookieContainer CookieContainer 例: SocketsHttpHandler プロパティの設定
Credentials Credentials 例: SocketsHttpHandler プロパティの設定
Date Date 例: 要求ヘッダーを設定する
DefaultCachePolicy 同等の API なし 例: CachePolicy ヘッダーを適用する
DefaultMaximumErrorResponseLength 同等の API なし 例: HttpClient で MaximumErrorResponseLength を設定する
DefaultMaximumResponseHeadersLength 同等の API なし 代わりに MaxResponseHeadersLength を使用できます。
DefaultWebProxy 同等の API なし 代わりに Proxy を使用できます。
Expect Expect 例: 要求ヘッダーを設定する
HaveResponse 同等の API なし HttpResponseMessage インスタンスを持つことによって暗黙的に指定されます。
Headers Headers 例: 要求ヘッダーを設定する
Host Host 例: 要求ヘッダーを設定する
IfModifiedSince IfModifiedSince 例: 要求ヘッダーを設定する
ImpersonationLevel 同等の API なし 例: ImpersonationLevel を変更する
KeepAlive 同等の API なし 例: 要求ヘッダーを設定する
MaximumAutomaticRedirections MaxAutomaticRedirections 例: SocketsHttpHandler プロパティの設定
MaximumResponseHeadersLength MaxResponseHeadersLength 例: SocketsHttpHandler プロパティの設定
MediaType 同等の API なし 例: コンテンツ ヘッダーを設定する
Method Method 例: HttpRequestMessage プロパティの使用
Pipelined 同等の API なし HttpClient ではパイプライン処理がサポートされていません。
PreAuthenticate PreAuthenticate
ProtocolVersion HttpRequestMessage.Version 例: HttpRequestMessage プロパティの使用
Proxy Proxy 例: SocketsHttpHandler プロパティの設定
ReadWriteTimeout 同等の API なし SocketsHttpHandler と ConnectCallback の使用
Referer Referrer 例: 要求ヘッダーを設定する
RequestUri RequestUri 例: HttpRequestMessage プロパティの使用
SendChunked TransferEncodingChunked 例: 要求ヘッダーを設定する
ServerCertificateValidationCallback RemoteCertificateValidationCallback= 例: SocketsHttpHandler プロパティの設定
ServicePoint 同等の API なし ServicePointHttpClient の一部ではありません。
SupportsCookieContainer 同等の API なし HttpClient の場合、これは常に true です。
Timeout Timeout
TransferEncoding TransferEncoding 例: 要求ヘッダーを設定する
UnsafeAuthenticatedConnectionSharing 同等の API なし 回避策なし
UseDefaultCredentials 同等の API なし 例: SocketsHttpHandler プロパティの設定
UserAgent UserAgent 例: 要求ヘッダーを設定する

ServicePoint(Manager) の使用状況の移行

ServicePointManager は静的クラスであるため、プロパティに加えられた変更は、アプリケーション内で新しく作成されたすべての ServicePoint オブジェクトにグローバルな影響を与える点に注意する必要があります。 たとえば、ConnectionLimitExpect100Continue などのプロパティに変更を加えると、すべての新しい ServicePoint インスタンスに影響します。

警告

最新の .NET において、HttpClient では ServicePointManager に設定された構成は考慮されません。

ServicePointManager プロパティのマッピング

ServicePointManager古い API 新しい API メモ
CheckCertificateRevocationList CertificateRevocationCheckMode= 例: SocketsHttpHandler を使用した CRL チェックの有効化
DefaultConnectionLimit MaxConnectionsPerServer 例: SocketsHttpHandler プロパティの設定
DnsRefreshTimeout 同等の API なし 例: Dns ラウンド ロビンの有効化
EnableDnsRoundRobin 同等の API なし 例: Dns ラウンド ロビンの有効化
EncryptionPolicy EncryptionPolicy= 例: SocketsHttpHandler プロパティの設定
Expect100Continue ExpectContinue 例: 要求ヘッダーを設定する
MaxServicePointIdleTime PooledConnectionIdleTimeout 例: SocketsHttpHandler プロパティの設定
MaxServicePoints 同等の API なし ServicePointHttpClient の一部ではありません。
ReusePort 同等の API なし SocketsHttpHandler と ConnectCallback の使用
SecurityProtocol EnabledSslProtocols= 例: SocketsHttpHandler プロパティの設定
ServerCertificateValidationCallback RemoteCertificateValidationCallback= これらはいずれも RemoteCertificateValidationCallback
UseNagleAlgorithm 同等の API なし SocketsHttpHandler と ConnectCallback の使用

警告

最新の .NET では、UseNagleAlgorithm プロパティと Expect100Continue プロパティの既定値が false に設定されています。 これらの値は、.NET Framework では既定で true でした。

ServicePointManager メソッドのマッピング

ServicePointManager古い API 新しい API メモ
FindServicePoint 同等の API なし 回避策なし
SetTcpKeepAlive 同等の API なし SocketsHttpHandler と ConnectCallback の使用

ServicePoint プロパティのマッピング

ServicePoint古い API 新しい API メモ
Address HttpRequestMessage.RequestUri これは要求 URI です。この情報は HttpRequestMessage にあります。
BindIPEndPointDelegate 同等の API なし SocketsHttpHandler と ConnectCallback の使用
Certificate 同等の API なし この情報は RemoteCertificateValidationCallback から取得できます。 例: 証明書を取得する
ClientCertificate 同等の API なし 例: 相互認証の有効化
ConnectionLeaseTimeout SocketsHttpHandler.PooledConnectionLifetime HttpClient の同等の設定
ConnectionLimit MaxConnectionsPerServer 例: SocketsHttpHandler プロパティの設定
ConnectionName 同等の API なし 回避策なし
CurrentConnections 同等の API なし .NET ドキュメントのネットワーキング テレメトリ
Expect100Continue ExpectContinue 例: 要求ヘッダーを設定する
IdleSince 同等の API なし 回避策なし
MaxIdleTime PooledConnectionIdleTimeout 例: SocketsHttpHandler プロパティの設定
ProtocolVersion HttpRequestMessage.Version 例: HttpRequestMessage プロパティの使用
ReceiveBufferSize 同等の API なし SocketsHttpHandler と ConnectCallback の使用
SupportsPipelining 同等の API なし HttpClient ではパイプライン処理がサポートされていません。
UseNagleAlgorithm 同等の API なし SocketsHttpHandler と ConnectCallback の使用

ServicePoint メソッドのマッピング

ServicePoint古い API 新しい API メモ
CloseConnectionGroup 同等の機能がありません 回避策なし
SetTcpKeepAlive 同等の API なし SocketsHttpHandler と ConnectCallback の使用

HttpClient プロパティと HttpRequestMessage プロパティの使用

.NET で HttpClient を使用する場合は、HTTP の要求と応答を構成およびカスタマイズできるさまざまなプロパティにアクセスできます。 これらのプロパティを理解することで、HttpClient を最大限に活用し、アプリケーションが Web サービスと効率的かつ安全に通信できるようになります。

例: HttpRequestMessage プロパティの使用

HttpClient と HttpRequestMessage を併用する方法の例を次に示します。

var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://example.com"); // Method and RequestUri usage
var request = new HttpRequestMessage() // Alternative way to set RequestUri and Method
{
    RequestUri = new Uri("https://example.com"),
    Method = HttpMethod.Post
};
request.Headers.Add("Custom-Header", "value");
request.Content = new StringContent("somestring");

using var response = await client.SendAsync(request);
var protocolVersion = response.RequestMessage.Version; // Fetch `ProtocolVersion`.

例: リダイレクトされた URI を取得する

リダイレクトされた URI を取得する方法の例を次に示します (HttpWebRequest.Address と同じ)。

var client = new HttpClient();
using var response = await client.GetAsync(uri);
var redirectedUri = response.RequestMessage.RequestUri;

SocketsHttpHandler と ConnectCallback の使用

SocketsHttpHandlerConnectCallback プロパティを使用すると、開発者は TCP 接続を確立するプロセスをカスタマイズできます。 これは、DNS 解決を制御したり、接続に特定のソケット オプションを適用したりする必要があるシナリオに役立ちます。 ConnectCallback を使用すると、接続プロセスをインターセプトして変更してから HttpClient で使用できます。

例: ソケットへの IP アドレスのバインド

HttpWebRequest を使用する以前のアプローチでは、カスタム ロジックを使用して特定の IP アドレスをソケットにバインドすることもありました。 HttpClientConnectCallback を使用して同様の機能を実現する方法を次に示します。

HttpWebRequest を使用した古いコード:

HttpWebRequest request = WebRequest.CreateHttp(uri);
request.ServicePoint.BindIPEndPointDelegate = (servicePoint, remoteEndPoint, retryCount) =>
{
    // Bind to a specific IP address
    IPAddress localAddress = IPAddress.Parse("192.168.1.100");
    return new IPEndPoint(localAddress, 0);
};
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();

HttpClientConnectCallback を使用した新しいコード:

var handler = new SocketsHttpHandler
{
    ConnectCallback = async (context, cancellationToken) =>
    {
        // Bind to a specific IP address
        IPAddress localAddress = IPAddress.Parse("192.168.1.100");
        var socket = new Socket(localAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        try
        {
            socket.Bind(new IPEndPoint(localAddress, 0));
            await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
            return new NetworkStream(socket, ownsSocket: true);
        }
        catch
        {
            socket.Dispose();
            throw;
        }
    }
};
var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);

例: 特定のソケット オプションの適用

TCP キープアライブの有効化など、特定のソケット オプションを適用する必要がある場合は、HttpClient で使用する前に、ConnectCallback を使用してソケットを構成できます。 実際、ConnectCallback の方がソケット オプションを柔軟に構成できます。

HttpWebRequest を使用した古いコード:

ServicePointManager.ReusePort = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
request.ServicePoint.SetTcpKeepAlive(true, 60000, 1000);
request.ServicePoint.ReceiveBufferSize = 8192;
request.ServicePoint.UseNagleAlgorithm = false;
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();

HttpClientConnectCallback を使用した新しいコード:

var handler = new SocketsHttpHandler
{
    ConnectCallback = async (context, cancellationToken) =>
    {
        var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
        try
        {
            // Setting TCP Keep Alive
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 60);
            socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 1);

            // Setting ReceiveBufferSize
            socket.ReceiveBufferSize = 8192;

            // Enabling ReusePort
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, true);

            // Disabling Nagle Algorithm
            socket.NoDelay = true;

            await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
            return new NetworkStream(socket, ownsSocket: true);
        }
        catch
        {
            socket.Dispose();
            throw;
        }
    }
};
var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);

例: DNS ラウンド ロビンを有効にする

DNS ラウンド ロビンは、1 つのドメイン名に関連付けられている IP アドレスのリストをローテーションして、複数のサーバー間でネットワーク トラフィックを分散するために使用される手法です。 これは、負荷分散とサービスの可用性の向上に役立ちます。 HttpClient を使用する場合、DNS ラウンド ロビンを実装するには、手動で DNS 解決を処理し、SocketsHttpHandler の ConnectCallback プロパティを使用して IP アドレスをローテーションします。

HttpClient で DNS ラウンド ロビンを有効にするには、ConnectCallback プロパティを使用して DNS エントリを手動で解決し、IP アドレスをローテーションします。 HttpWebRequestHttpClient の例を次に示します。

HttpWebRequest を使用した古いコード:

ServicePointManager.DnsRefreshTimeout = 60000;
ServicePointManager.EnableDnsRoundRobin = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();

以前の HttpWebRequest API では、この機能の組み込みサポートにより、DNS ラウンド ロビンの有効化は簡単でした。 ただし、新しい HttpClient API では同じ組み込み機能は提供されていません。 それでも、DNS 解決によって返される IP アドレスを手動でローテーションする DnsRoundRobinConnector を実装することで、同様の動作を実現できます。

HttpClient を使用した新しいコード:

// This is available as NuGet Package: https://www.nuget.org/packages/DnsRoundRobin/
// The original source code can be found also here: https://github.com/MihaZupan/DnsRoundRobin
public sealed class DnsRoundRobinConnector : IDisposable

DnsRoundRobinConnector の実装についてはこちらを参照してください。

DnsRoundRobinConnector使用:

private static readonly DnsRoundRobinConnector s_roundRobinConnector = new(
        dnsRefreshInterval: TimeSpan.FromSeconds(10),
        endpointConnectTimeout: TimeSpan.FromSeconds(5));
static async Task DnsRoundRobinConnectAsync()
{
    var handler = new SocketsHttpHandler
    {
        ConnectCallback = async (context, cancellation) =>
        {
            Socket socket = await DnsRoundRobinConnector.Shared.ConnectAsync(context.DnsEndPoint, cancellation);
            // Or you can create and use your custom DnsRoundRobinConnector instance
            // Socket socket = await s_roundRobinConnector.ConnectAsync(context.DnsEndPoint, cancellation);
            return new NetworkStream(socket, ownsSocket: true);
        }
    };
    var client = new HttpClient(handler);
    HttpResponseMessage response = await client.GetAsync(Uri);
}

例: SocketsHttpHandler プロパティの設定

SocketsHttpHandler は、HTTP 接続を管理するための高度な構成オプションを提供する、.NET の強力で柔軟なハンドラーです。 SocketsHttpHandler のさまざまなプロパティを設定することで、パフォーマンスの最適化、セキュリティの強化、カスタム接続処理など、特定の要件を満たすように HTTP クライアントの動作を微調整できます。

さまざまなプロパティで SocketsHttpHandler を構成し、HttpClient で使用する方法の例を次に示します。

var cookieContainer = new CookieContainer();
cookieContainer.Add(new Cookie("cookieName", "cookieValue"));

var handler = new SocketsHttpHandler
{
    AllowAutoRedirect = true,
    AutomaticDecompression = DecompressionMethods.All,
    Expect100ContinueTimeout = TimeSpan.FromSeconds(1),
    CookieContainer = cookieContainer,
    Credentials = new NetworkCredential("user", "pass"),
    MaxAutomaticRedirections = 10,
    MaxResponseHeadersLength = 1,
    Proxy = new WebProxy("http://proxyserver:8080"), // Don't forget to set UseProxy
    UseProxy = true,
};

var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);

例: ImpersonationLevel を変更する

この機能は特定のプラットフォームに固有であり、やや古くなっています。 回避策が必要な場合は、コードのこのセクションを参照してください。

HttpClient を使用する場合は、サーバー証明書のカスタム検証やサーバー証明書の取得など、さまざまな目的でクライアント証明書を処理することが必要になる場合があります。 HttpClient には、証明書を効率的に管理するためのプロパティとオプションがいくつか用意されています。

例: SocketsHttpHandler を使用して証明書失効リストをチェックする

SocketsHttpHandler.SslOptionsCheckCertificateRevocationList プロパティを使用すると、開発者は SSL/TLS ハンドシェイク中に証明書失効リスト (CRL) のチェックを有効または無効にすることができます。 このプロパティを有効にすると、クライアントはサーバーの証明書が失効しているかどうかを確認するため、接続のセキュリティが強化されます。

HttpWebRequest を使用した古いコード:

ServicePointManager.CheckCertificateRevocationList = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();

HttpClient を使用した新しいコード:

bool checkCertificateRevocationList = true;
var handler = new SocketsHttpHandler
{
    SslOptions =
    {
        CertificateRevocationCheckMode = checkCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
    }
};
var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);

例: 証明書を取得する

HttpClientRemoteCertificateValidationCallback から証明書を取得するには、HttpClientHandler または SocketsHttpHandler.SslOptionsServerCertificateCustomValidationCallback プロパティを使用します。 このコールバックを使用すると、SSL/TLS ハンドシェイク中にサーバーの証明書を検査できます。

HttpWebRequest を使用した古いコード:

HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
X509Certificate? serverCertificate = request.ServicePoint.Certificate;

HttpClient を使用した新しいコード:

X509Certificate? serverCertificate = null;
var handler = new SocketsHttpHandler
{
    SslOptions = new SslClientAuthenticationOptions
    {
        RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
        {
            serverCertificate = certificate;

            // Leave the validation as-is.
            return sslPolicyErrors == SslPolicyErrors.None;
        }
    }
};
var client = new HttpClient(handler);
using var response = await client.GetAsync("https://example.com");

例: 相互認証を有効にする

相互認証 (双方向 SSL またはクライアント証明書認証とも呼ばれます) は、クライアントとサーバーの両方が相互に認証を行うセキュリティ プロセスです。 これにより、両当事者の本人確認が行われ、機密性の高い通信のセキュリティが強化されます。 HttpClient では、クライアント証明書を含めてサーバーの証明書を検証するように HttpClientHandler または SocketsHttpHandler を構成することで、相互認証を有効にすることができます。

相互認証を有効にするには、次の手順を実行します。

  • クライアント証明書を読み込みます。
  • クライアント証明書を含むように HttpClientHandler または SocketsHttpHandler を構成します。
  • カスタム検証が必要な場合は、サーバー証明書検証コールバックを設定します。

SocketsHttpHandler を使用した例を次に示します。

var handler = new SocketsHttpHandler
{
    SslOptions = new SslClientAuthenticationOptions
    {
        ClientCertificates = new X509CertificateCollection
        {
            // Load the client certificate from a file
            new X509Certificate2("path_to_certificate.pfx", "certificate_password")
        },
        RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
        {
            // Custom validation logic for the server certificate
            return sslPolicyErrors == SslPolicyErrors.None;
        }
    }
};

var client = new HttpClient(handler);
using var response = await client.GetAsync(uri);

ヘッダー プロパティの使用

ヘッダーは HTTP 通信において重要な役割を果たし、要求と応答に関する必要不可欠なメタデータを提供します。 .NET で HttpClient を使用する場合は、さまざまなヘッダー プロパティを設定および管理して、HTTP の要求と応答の動作を制御できます。 これらのヘッダー プロパティを効果的に使用する方法を理解すると、アプリケーションが Web サービスと効率的かつ安全に通信できるようになります。

要求ヘッダーを設定する

要求ヘッダーは、行われる要求に関する追加情報をサーバーに提供するために使用されます。 一般的なユース ケースとしては、コンテンツ タイプの指定、認証トークンの設定、カスタム ヘッダーの追加などがあります。 要求ヘッダーは、HttpClientDefaultRequestHeaders プロパティまたは HttpRequestMessage の Headers プロパティを使用して設定できます。

例: カスタム要求ヘッダーを設定する

HttpClient での既定のカスタム要求ヘッダーの設定

var client = new HttpClient();
client.DefaultRequestHeaders.Add("Custom-Header", "value");

HttpRequestMessage でのカスタム要求ヘッダーの設定

var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Custom-Header", "value");

例: 共通要求ヘッダーを設定する

.NET で HttpRequestMessage を使用する場合、行われる要求に関する追加情報をサーバーに提供するには、共通要求ヘッダーを設定することが不可欠です。 これらのヘッダーには認証トークンなどを含めることができます。 これらのヘッダーを適切に構成すると、サーバーによって HTTP 要求が正しく処理されます。 HttpRequestHeaders で使用できる共通プロパティの包括的なリストについては、「プロパティ」を参照してください。

HttpRequestMessageで共通要求ヘッダーを設定するには、HttpRequestMessage オブジェクトの Headers プロパティを使用します。 このプロパティは、必要に応じてヘッダーを追加または変更できる HttpRequestHeaders コレクションへのアクセスを提供します。

HttpClient での共通の既定要求ヘッダーの設定

using System.Net.Http.Headers;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "token");

HttpRequestMessage での共通要求ヘッダーの設定

using System.Net.Http.Headers;

var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token");

例: コンテンツ ヘッダーを設定する

コンテンツ ヘッダーは、HTTP の要求または応答の本文に関する追加情報を提供するために使用されます。 .NET で HttpClient を使用する場合は、コンテンツ ヘッダーを設定して、送受信するコンテンツに関連するメディアの種類、エンコード、およびその他のメタデータを指定できます。 コンテンツ ヘッダーを適切に構成すると、サーバーとクライアントがコンテンツを正しく解釈して処理できるようになります。

var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, uri);

// Create the content and set the content headers
var jsonData = "{\"key\":\"value\"}";
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");

// The following headers are set automatically by `StringContent`. If you wish to override their values, you can do it like so:
// content.Headers.ContentType = new MediaTypeHeaderValue("application/json; charset=utf-8");
// content.Headers.ContentLength = Encoding.UTF8.GetByteCount(jsonData);

// Assign the content to the request
request.Content = content;

using var response = await client.SendAsync(request);

例: HttpClient で MaximumErrorResponseLength を設定する

MaximumErrorResponseLength を使用することで、ハンドラーがバッファリングするエラー応答コンテンツの最大長を開発者が指定できます。 これは、エラー応答がサーバーから受信されたときにメモリに読み取られて格納されるデータの量を制御するのに役立ちます。 この手法を使用すると、大量のエラー応答を処理するときに、過剰なメモリの使用を防ぎ、アプリケーションのパフォーマンスを向上させることができます。

これを行う方法はいくつかあります。この例では TruncatedReadStream 手法について説明します。

internal sealed class TruncatedReadStream(Stream innerStream, long maxSize) : Stream
{
    private long _maxRemainingLength = maxSize;
    public override bool CanRead => true;
    public override bool CanSeek => false;
    public override bool CanWrite => false;

    public override long Length => throw new NotSupportedException();
    public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }

    public override void Flush() => throw new NotSupportedException();

    public override int Read(byte[] buffer, int offset, int count)
    {
        return Read(new Span<byte>(buffer, offset, count));
    }

    public override int Read(Span<byte> buffer)
    {
        int readBytes = innerStream.Read(buffer.Slice(0, (int)Math.Min(buffer.Length, _maxRemainingLength)));
        _maxRemainingLength -= readBytes;
        return readBytes;
    }

    public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
    }

    public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
    {
        int readBytes = await innerStream.ReadAsync(buffer.Slice(0, (int)Math.Min(buffer.Length, _maxRemainingLength)), cancellationToken)
            .ConfigureAwait(false);
        _maxRemainingLength -= readBytes;
        return readBytes;
    }

    public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
    public override void SetLength(long value) => throw new NotSupportedException();
    public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();

    public override ValueTask DisposeAsync() => innerStream.DisposeAsync();

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            innerStream.Dispose();
        }
    }
}

TruncatedReadStream の使用例:

int maxErrorResponseLength = 1 * 1024; // 1 KB

HttpClient client = new HttpClient();
using HttpResponseMessage response = await client.GetAsync(Uri);

if (response.Content is not null)
{
    Stream responseReadStream = await response.Content.ReadAsStreamAsync();
    // If MaxErrorResponseLength is set and the response status code is an error code, then wrap the response stream in a TruncatedReadStream
    if (maxErrorResponseLength >= 0 && !response.IsSuccessStatusCode)
    {
        responseReadStream = new TruncatedReadStream(responseReadStream, maxErrorResponseLength);
    }
    // Read the response stream
    Memory<byte> buffer = new byte[1024];
    int readValue = await responseReadStream.ReadAsync(buffer);
}

例: CachePolicy ヘッダーを適用する

警告

HttpClient には応答をキャッシュするロジックが組み込まれていません。 すべてのキャッシュを自分で実装する以外の回避策はありません。 ヘッダーを設定するだけではキャッシュは実現されません。

HttpWebRequest から HttpClient に移行する場合は、pragmacache-control などのキャッシュ関連のヘッダーを正しく処理することが重要です。 これらのヘッダーは応答のキャッシュ方法と取得方法を制御し、パフォーマンスとデータの鮮度の観点からアプリケーションが期待どおりに動作することを保証します。

HttpWebRequest では、CachePolicy プロパティを使用してこれらのヘッダーを設定していた場合もあります。 ただし、HttpClient では、要求でこれらのヘッダーを手動で設定する必要があります。

HttpWebRequest を使用した古いコード:

HttpWebRequest request = WebRequest.CreateHttp(uri);
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();

以前の HttpWebRequest API では、この機能の組み込みサポートにより CachePolicy の適用は簡単でした。 ただし、新しい HttpClient API では同じ組み込み機能は提供されていません。 それでも、キャッシュ関連のヘッダーを手動で追加する AddCacheControlHeaders を実装することで、同様の動作を実現できます。

HttpClient を使用した新しいコード:

public static class CachePolicy
{
    public static void AddCacheControlHeaders(HttpRequestMessage request, RequestCachePolicy policy)

AddCacheControlHeaders の実装についてはこちらを参照してください。

AddCacheControlHeaders使用:

static async Task AddCacheControlHeaders()
{
    HttpClient client = new HttpClient();
    HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, Uri);
    CachePolicy.AddCacheControlHeaders(requestMessage, new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore));
    HttpResponseMessage response = await client.SendAsync(requestMessage);
}

バッファリング プロパティの使用

HttpWebRequest から HttpClient に移行する場合は、これら 2 つの API におけるバッファリング方法の違いを理解することが重要です。

HttpWebRequest を使用した古いコード:

HttpWebRequest では、AllowWriteStreamBuffering プロパティと AllowReadStreamBuffering プロパティを使用してバッファリング プロパティを直接制御できます。 これらのプロパティは、サーバーとの間で送受信されるデータのバッファリングを有効または無効にします。

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AllowReadStreamBuffering = true; // Default is `false`.
request.AllowWriteStreamBuffering = false; // Default is `true`.

HttpClient を使用した新しいコード:

HttpClient では、AllowWriteStreamBuffering プロパティと AllowReadStreamBuffering プロパティに直接相当するものはありません。

HttpClient は、要求本文を単独でバッファリングするのではなく、使用される HttpContent に責任を委任します。 StringContentByteArrayContent などのコンテンツは論理的に既にメモリにバッファリングされていますが、StreamContent を使用しても、既定ではバッファリングは発生しません。 コンテンツを強制的にバッファリングするには、要求を送信する前に HttpContent.LoadIntoBufferAsync を呼び出します。 次に例を示します。

HttpClient client = new HttpClient();

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = new StreamContent(yourStream);
await request.Content.LoadIntoBufferAsync();

HttpResponseMessage response = await client.SendAsync(request);

HttpClient では、既定で読み取りバッファリングが有効になっています。 これを回避するには、HttpCompletionOption.ResponseHeadersRead フラグを指定するか、GetStreamAsync ヘルパーを使用します。

HttpClient client = new HttpClient();

using HttpResponseMessage response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
await using Stream responseStream = await response.Content.ReadAsStreamAsync();

// Or simply
await using Stream responseStream = await client.GetStreamAsync(uri);