Поделиться через


Руководство по миграции HttpWebRequest в HttpClient

Эта статья направлена на руководство разработчиков по процессу миграции с HttpWebRequest, ServicePointа также ServicePointManager на HttpClient. Миграция необходима из-за устаревания старых API и многочисленных преимуществ, предлагаемых HttpClient, включая повышение производительности, улучшение управления ресурсами и более современную и гибкую структуру API. Следуя инструкциям, описанным в этом документе, разработчики смогут плавно перенести свои базы кода и воспользоваться всеми преимуществами функций, предоставляемых HttpClientв этом документе.

Предупреждение

Переход с HttpWebRequest, ServicePointи ServicePointManager не HttpClient только "приятно иметь" повышение производительности. Важно понимать, что производительность существующей WebRequest логики может значительно снизиться после перехода на .NET (Core). Это связано с тем, что WebRequest поддерживается как минимальный уровень совместимости, что означает, что он не имеет большого количества оптимизаций, таких как повторное использование подключения во многих случаях. Таким образом, переход HttpClient на важный для обеспечения производительности и управления ресурсами приложения соответствует современным стандартам.

Переход из HttpWebRequestHttpClient

Начнем с некоторых примеров:

Простой запрос GET с помощью HttpWebRequest

Ниже приведен пример того, как может выглядеть код:

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

Простой запрос GET с помощью HttpClient

Ниже приведен пример того, как может выглядеть код:

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

Простой запрос POST с помощью HttpWebRequest

Ниже приведен пример того, как может выглядеть код:

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();

Простой запрос POST с помощью HttpClient

Ниже приведен пример того, как может выглядеть код:

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 SslOptions.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 Пример. Задание MaximumErrorResponseLength в HttpClient.
DefaultMaximumResponseHeadersLength Нет эквивалентного API MaxResponseHeadersLength вместо этого можно использовать.
DefaultWebProxy Нет эквивалентного API Proxy вместо этого можно использовать.
Expect Expect Пример. Установка заголовков запросов.
HaveResponse Нет эквивалентного API Подразумевается наличие экземпляра HttpResponseMessage .
Headers Headers Пример. Установка заголовков запросов.
Host Host Пример. Установка заголовков запросов.
IfModifiedSince IfModifiedSince Пример. Установка заголовков запросов.
ImpersonationLevel Нет прямого эквивалентного API Пример: изменение олицетворенияLevel.
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 SslOptions.RemoteCertificateValidationCallback Пример. Задание свойств SocketsHttpHandler.
ServicePoint Нет эквивалентного API ServicePoint не является частью HttpClient.
SupportsCookieContainer Нет эквивалентного API Это всегда для true HttpClient.
Timeout Timeout
TransferEncoding TransferEncoding Пример. Установка заголовков запросов.
UnsafeAuthenticatedConnectionSharing Нет эквивалентного API Обходной путь
UseDefaultCredentials Нет прямого эквивалентного API Пример. Задание свойств SocketsHttpHandler.
UserAgent UserAgent Пример. Установка заголовков запросов.

Перенос использования ServicePoint(Manager)

Следует учитывать, что ServicePointManager является статическим классом, то есть любые изменения, внесенные в его свойства, будут иметь глобальное влияние на все вновь созданные ServicePoint объекты в приложении. Например, при изменении свойства, например ConnectionLimit , Expect100Continueэто влияет на каждый новый экземпляр ServicePoint.

Предупреждение

В современной .NET HttpClient не учитывает конфигурации, установленные в ServicePointManager.

ServicePointManager Сопоставление свойств

ServicePointManager Старый API Новый API Примечания.
CheckCertificateRevocationList SslOptions.CertificateRevocationCheckMode Пример. Включение проверки списка отзыва сертификатов с помощью SocketsHttpHandler.
DefaultConnectionLimit MaxConnectionsPerServer Пример. Задание свойств SocketsHttpHandler.
DnsRefreshTimeout Нет эквивалентного API Пример: включение dns-циклического робина.
EnableDnsRoundRobin Нет эквивалентного API Пример: включение dns-циклического робина.
EncryptionPolicy SslOptions.EncryptionPolicy Пример. Задание свойств SocketsHttpHandler.
Expect100Continue ExpectContinue Пример. Установка заголовков запросов.
MaxServicePointIdleTime PooledConnectionIdleTimeout Пример. Задание свойств SocketsHttpHandler.
MaxServicePoints Нет эквивалентного API ServicePoint не является частью HttpClient.
ReusePort Нет прямого эквивалентного API Использование SocketsHttpHandler и ConnectCallback.
SecurityProtocol SslOptions.EnabledSslProtocols Пример. Задание свойств SocketsHttpHandler.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Оба из них являются RemoteCertificateValidationCallback
UseNagleAlgorithm Нет прямого эквивалентного API Использование SocketsHttpHandler и ConnectCallback.

Предупреждение

В современном .NET значения по умолчанию для UseNagleAlgorithm свойств и Expect100Continue свойств заданы false. Эти значения были true по умолчанию в платформа .NET Framework.

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

При работе с HttpClient в .NET у вас есть доступ к различным свойствам, которые позволяют настраивать и настраивать HTTP-запросы и ответы. Общие сведения об этих свойствах помогут вам получить большую часть HttpClient и обеспечить эффективную и безопасную связь приложения с веб-службами.

Пример. Использование свойств 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

Это ConnectCallback свойство SocketsHttpHandler позволяет разработчикам настраивать процесс установления TCP-подключения. Это может быть полезно для сценариев, когда необходимо управлять разрешением DNS или применять определенные параметры сокета к подключению. С помощью ConnectCallbackможно перехватывать и изменять процесс подключения, прежде чем он будет использоваться HttpClient.

Пример. Привязка IP-адреса к сокету

В старом подходе, HttpWebRequestвозможно, вы использовали пользовательскую логику для привязки определенного IP-адреса к сокету. Вот как можно реализовать аналогичные функциональные возможности с помощью HttpClient и ConnectCallback:

Старый код с помощью 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();

Использование нового кода HttpClient и ConnectCallback:

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, можно использовать ConnectCallback для настройки сокета, прежде чем он будет использоваться HttpClient. На самом деле, 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();

Использование нового кода HttpClient и ConnectCallback:

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 Round Robin — это метод, используемый для распределения сетевого трафика между несколькими серверами путем поворота списка IP-адресов, связанных с одним доменным именем. Это помогает в балансировке нагрузки и повышении доступности служб. При использовании HttpClient можно реализовать DNS Round Robin, вручную обрабатывая разрешение DNS и вращаясь по IP-адресам с помощью свойства ConnectCallback SocketsHttpHandler.

Чтобы включить DNS Round Robin с помощью HttpClient, можно использовать свойство ConnectCallback для ручного разрешения записей DNS и поворота через IP-адреса. Ниже приведен пример HttpWebRequest для и HttpClient:

Старый код с помощью HttpWebRequest:

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

В более старой HttpWebRequest версии API включение DNS Round Robin было простым из-за встроенной поддержки этой функции. Однако более новый HttpClient API не предоставляет одни и те же встроенные функции. Несмотря на это, можно добиться аналогичного поведения, реализовав DnsRoundRobinConnector , что вручную поворачивается через IP-адреса, возвращаемые разрешением DNS.

Новый код с помощью 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 — это мощный и гибкий обработчик в .NET, который предоставляет расширенные параметры конфигурации для управления HTTP-подключениями. Задав различные свойства 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);

Пример. Изменение олицетворенияLevel

Эта функция зависит от определенных платформ и несколько устарела. Если вам нужен обходной путь, обратитесь к этому разделу кода.

При работе HttpClientс ними может потребоваться обрабатывать сертификаты клиента для различных целей, например настраиваемую проверку сертификатов сервера или получение сертификата сервера. HttpClient предоставляет несколько свойств и вариантов эффективного управления сертификатами.

Пример. Проверка списка отзыва сертификатов с помощью SocketsHttpHandler

Это CheckCertificateRevocationList свойство SocketsHttpHandler.SslOptions позволяет разработчикам включить или отключить проверку списков отзыва сертификатов (CRL) во время подтверждения SSL/TLS. Включение этого свойства гарантирует, что клиент проверяет, был ли отозван сертификат сервера, повышая безопасность подключения.

Старый код с помощью 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);

Пример: получение сертификата

Чтобы получить сертификат из in RemoteCertificateValidationCallback HttpClient, можно использовать ServerCertificateCustomValidationCallback свойство HttpClientHandler или SocketsHttpHandler.SslOptions. Этот обратный вызов позволяет проверить сертификат сервера во время подтверждения 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, предоставляя необходимые метаданные о запросе и ответе. При работе с HttpClient .NET можно задать различные свойства заголовка и управлять ими, чтобы управлять поведением HTTP-запросов и ответов. Понимание того, как эффективно использовать эти свойства заголовков, поможет обеспечить эффективное и безопасное взаимодействие приложения с веб-службами.

Установка заголовков запросов

Заголовки запросов используются для предоставления дополнительных сведений серверу о выполняемом запросе. Распространенные варианты использования включают указание типа контента, настройка маркеров проверки подлинности и добавление пользовательских заголовков. Вы можете задать заголовки запросов с помощью DefaultRequestHeaders свойства HttpClient или свойства HttpRequestMessageHeaders .

Пример. Настройка заголовков пользовательских запросов

Настройка заголовков пользовательских запросов по умолчанию в 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");

Пример. Настройка общих заголовков запросов

При работе с HttpRequestMessage .NET настройка общих заголовков запросов необходима для предоставления дополнительных сведений серверу о выполняемом запросе. Эти заголовки могут включать маркеры проверки подлинности и многое другое. Правильная настройка этих заголовков гарантирует правильность обработки HTTP-запросов сервером. Полный список общих свойств, доступных в HttpRequestHeadersразделе "Свойства".

Чтобы задать общие заголовки HttpRequestMessageзапросов, можно использовать Headers свойство HttpRequestMessage объекта. Это свойство предоставляет доступ к 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);

Пример. Задание MaximumErrorResponseLength в HttpClient

Использование 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нее важно правильно обрабатывать заголовки, связанные с кэшем, pragma например и cache-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в , важно понимать различия в том, как эти два 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 . Содержимое, подобное или ByteArrayContent логическое, уже буферизованное в памяти, в то время как StringContent использование 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);