Partilhar via


Guia de migração de HttpWebRequest para HttpClient

Este artigo tem como objetivo orientar os desenvolvedores através do processo de migração de HttpWebRequest, ServicePointe ServicePointManager para HttpClient. A migração é necessária devido à obsolescência das APIs mais antigas e aos inúmeros benefícios oferecidos pela HttpClient, incluindo melhor desempenho, melhor gerenciamento de recursos e um design de API mais moderno e flexível. Seguindo as etapas descritas neste documento, os desenvolvedores poderão fazer a transição de suas bases de código sem problemas e aproveitar ao máximo os recursos fornecidos pelo HttpClient.

Aviso

Migrar de , ServicePointe ServicePointManager para HttpClient não é apenas uma melhoria de HttpWebRequestdesempenho "agradável de ter". É crucial entender que o desempenho da lógica existente WebRequest provavelmente se degradará significativamente quando você mudar para o .NET (Core). Isso porque WebRequest é mantido como uma camada mínima de compatibilidade, o que significa que carece de muitas otimizações, como a reutilização de conexão em vários casos. Portanto, a transição para HttpClient é essencial para garantir que o desempenho do seu aplicativo e o gerenciamento de recursos estejam de acordo com os padrões modernos.

Migrar de HttpWebRequest para HttpClient

Comecemos por alguns exemplos:

Solicitação GET simples usando HttpWebRequest

Aqui está um exemplo de como o código pode parecer:

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

Solicitação GET simples usando HttpClient

Aqui está um exemplo de como o código pode parecer:

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

Solicitação POST simples usando HttpWebRequest

Aqui está um exemplo de como o código pode parecer:

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

Solicitação POST simples usando HttpClient

Aqui está um exemplo de como o código pode parecer:

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

HttpWebRequest para HttpClient, guia de migração SocketsHttpHandler

HttpWebRequest API antiga Nova API Notas
Accept Accept Exemplo: Definir cabeçalhos de solicitação.
Address RequestUri Exemplo: Buscar URI redirecionado.
AllowAutoRedirect AllowAutoRedirect Exemplo: Definindo propriedades SocketsHttpHandler.
AllowReadStreamBuffering Nenhuma API equivalente direta Uso de propriedades de buffer.
AllowWriteStreamBuffering Nenhuma API equivalente direta Uso de propriedades de buffer.
AuthenticationLevel Nenhuma API equivalente direta Exemplo: Habilitando a autenticação mútua.
AutomaticDecompression AutomaticDecompression Exemplo: Definindo propriedades SocketsHttpHandler.
CachePolicy Nenhuma API equivalente direta Exemplo: Aplicar cabeçalhos CachePolicy.
ClientCertificates SslOptions.ClientCertificates Uso de propriedades relacionadas ao certificado em HttpClient.
Connection Connection Exemplo: Definir cabeçalhos de solicitação.
ConnectionGroupName Nenhuma API equivalente Sem solução alternativa
ContentLength ContentLength Exemplo: definir cabeçalhos de conteúdo.
ContentType ContentType Exemplo: definir cabeçalhos de conteúdo.
ContinueDelegate Nenhuma API equivalente Nenhuma solução alternativa.
ContinueTimeout Expect100ContinueTimeout Exemplo: Definir propriedades SocketsHttpHandler.
CookieContainer CookieContainer Exemplo: Definir propriedades SocketsHttpHandler.
Credentials Credentials Exemplo: Definir propriedades SocketsHttpHandler.
Date Date Exemplo: Definir cabeçalhos de solicitação.
DefaultCachePolicy Nenhuma API equivalente direta Exemplo: Aplicar cabeçalhos CachePolicy.
DefaultMaximumErrorResponseLength Nenhuma API equivalente direta Exemplo: Defina MaximumErrorResponseLength em HttpClient.
DefaultMaximumResponseHeadersLength Nenhuma API equivalente MaxResponseHeadersLength pode ser usado em vez disso.
DefaultWebProxy Nenhuma API equivalente Proxy pode ser usado em vez disso.
Expect Expect Exemplo: Definir cabeçalhos de solicitação.
HaveResponse Nenhuma API equivalente Implícito por ter uma HttpResponseMessage instância.
Headers Headers Exemplo: Definir cabeçalhos de solicitação.
Host Host Exemplo: Definir cabeçalhos de solicitação.
IfModifiedSince IfModifiedSince Exemplo: Definir cabeçalhos de solicitação.
ImpersonationLevel Nenhuma API equivalente direta Exemplo: Change ImpersonationLevel.
KeepAlive Nenhuma API equivalente direta Exemplo: Definir cabeçalhos de solicitação.
MaximumAutomaticRedirections MaxAutomaticRedirections Exemplo: Definindo propriedades SocketsHttpHandler.
MaximumResponseHeadersLength MaxResponseHeadersLength Exemplo: Definindo propriedades SocketsHttpHandler.
MediaType Nenhuma API equivalente direta Exemplo: definir cabeçalhos de conteúdo.
Method Method Exemplo: Uso de propriedades HttpRequestMessage.
Pipelined Nenhuma API equivalente HttpClient não suporta pipelining.
PreAuthenticate PreAuthenticate
ProtocolVersion HttpRequestMessage.Version Exemplo: Uso de propriedades HttpRequestMessage.
Proxy Proxy Exemplo: Definindo propriedades SocketsHttpHandler.
ReadWriteTimeout Nenhuma API equivalente direta Uso de SocketsHttpHandler e ConnectCallback.
Referer Referrer Exemplo: Definir cabeçalhos de solicitação.
RequestUri RequestUri Exemplo: Uso de propriedades HttpRequestMessage.
SendChunked TransferEncodingChunked Exemplo: Definir cabeçalhos de solicitação.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Exemplo: Definindo propriedades SocketsHttpHandler.
ServicePoint Nenhuma API equivalente ServicePoint não faz parte do HttpClient.
SupportsCookieContainer Nenhuma API equivalente Isto é sempre true para HttpClient.
Timeout Timeout
TransferEncoding TransferEncoding Exemplo: Definir cabeçalhos de solicitação.
UnsafeAuthenticatedConnectionSharing Nenhuma API equivalente Sem solução alternativa
UseDefaultCredentials Nenhuma API equivalente direta Exemplo: Definindo propriedades SocketsHttpHandler.
UserAgent UserAgent Exemplo: Definir cabeçalhos de solicitação.

Migrar o uso do ServicePoint(Manager)

Você deve estar ciente de que ServicePointManager é uma classe estática, o que significa que quaisquer alterações feitas em suas propriedades terão um efeito global em todos os objetos recém-criados ServicePoint dentro do aplicativo. Por exemplo, quando você modifica uma propriedade como ConnectionLimit ou Expect100Continue, isso afeta cada nova instância do ServicePoint.

Aviso

No .NET moderno, HttpClient não leva em conta quaisquer configurações definidas em ServicePointManager.

ServicePointManager Mapeamento de propriedades

ServicePointManager API antiga Nova API Notas
CheckCertificateRevocationList SslOptions.CertificateRevocationCheckMode Exemplo: Habilitando a verificação de CRL com SocketsHttpHandler.
DefaultConnectionLimit MaxConnectionsPerServer Exemplo: Definindo propriedades SocketsHttpHandler.
DnsRefreshTimeout Nenhuma API equivalente Exemplo: Ativando o Dns Round Robin.
EnableDnsRoundRobin Nenhuma API equivalente Exemplo: Ativando o Dns Round Robin.
EncryptionPolicy SslOptions.EncryptionPolicy Exemplo: Definindo propriedades SocketsHttpHandler.
Expect100Continue ExpectContinue Exemplo: Definir cabeçalhos de solicitação.
MaxServicePointIdleTime PooledConnectionIdleTimeout Exemplo: Definindo propriedades SocketsHttpHandler.
MaxServicePoints Nenhuma API equivalente ServicePoint não faz parte do HttpClient.
ReusePort Nenhuma API equivalente direta Uso de SocketsHttpHandler e ConnectCallback.
SecurityProtocol SslOptions.EnabledSslProtocols Exemplo: Definindo propriedades SocketsHttpHandler.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Ambos são RemoteCertificateValidationCallback
UseNagleAlgorithm Nenhuma API equivalente direta Uso de SocketsHttpHandler e ConnectCallback.

Aviso

No .NET moderno, os valores padrão para as UseNagleAlgorithm propriedades e Expect100Continue são definidos como false. Esses valores eram true por padrão no .NET Framework.

ServicePointManager mapeamento de método

ServicePointManager API antiga Nova API Notas
FindServicePoint Nenhuma API equivalente Sem solução alternativa
SetTcpKeepAlive Nenhuma API equivalente direta Uso de SocketsHttpHandler e ConnectCallback.

ServicePoint Mapeamento de propriedades

ServicePoint API antiga Nova API Notas
Address HttpRequestMessage.RequestUri Esta é a solicitação uri, esta informação pode ser encontrada em HttpRequestMessage.
BindIPEndPointDelegate Nenhuma API equivalente direta Uso de SocketsHttpHandler e ConnectCallback.
Certificate Nenhuma API equivalente direta Essas informações podem ser obtidas em RemoteCertificateValidationCallback. Exemplo: Buscar certificado.
ClientCertificate Nenhuma API equivalente Exemplo: Habilitando a autenticação mútua.
ConnectionLeaseTimeout SocketsHttpHandler.PooledConnectionLifetime Configuração equivalente em HttpClient
ConnectionLimit MaxConnectionsPerServer Exemplo: Definindo propriedades SocketsHttpHandler.
ConnectionName Nenhuma API equivalente Sem solução alternativa
CurrentConnections Nenhuma API equivalente Consulte Telemetria de rede no .NET.
Expect100Continue ExpectContinue Exemplo: Definir cabeçalhos de solicitação.
IdleSince Nenhuma API equivalente Sem solução alternativa
MaxIdleTime PooledConnectionIdleTimeout Exemplo: Definindo propriedades SocketsHttpHandler.
ProtocolVersion HttpRequestMessage.Version Exemplo: Uso de propriedades HttpRequestMessage.
ReceiveBufferSize Nenhuma API equivalente direta Uso de SocketsHttpHandler e ConnectCallback.
SupportsPipelining Nenhuma API equivalente HttpClient não suporta pipelining.
UseNagleAlgorithm Nenhuma API equivalente direta Uso de SocketsHttpHandler e ConnectCallback.

ServicePoint mapeamento de método

ServicePoint API antiga Nova API Notas
CloseConnectionGroup Sem equivalente Sem solução alternativa
SetTcpKeepAlive Nenhuma API equivalente direta Uso de SocketsHttpHandler e ConnectCallback.

Uso das propriedades HttpClient e HttpRequestMessage

Ao trabalhar com HttpClient no .NET, você tem acesso a uma variedade de propriedades que permitem configurar e personalizar solicitações e respostas HTTP. Compreender essas propriedades pode ajudá-lo a aproveitar ao máximo o HttpClient e garantir que seu aplicativo se comunique de forma eficiente e segura com serviços da Web.

Exemplo: Uso de propriedades HttpRequestMessage

Aqui está um exemplo de como usar HttpClient e HttpRequestMessage juntos:

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`.

Exemplo: Buscar URI redirecionado

Aqui está um exemplo de como buscar URI redirecionado (o mesmo que HttpWebRequest.Address):

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

Uso de SocketsHttpHandler e ConnectCallback

A ConnectCallback propriedade in SocketsHttpHandler permite que os desenvolvedores personalizem o processo de estabelecimento de uma conexão TCP. Isso pode ser útil para cenários em que você precisa controlar a resolução DNS ou aplicar opções de soquete específicas na conexão. ConnectCallbackUsando o , você pode intercetar e modificar o processo de conexão antes que ele seja usado pelo HttpClient.

Exemplo: Vincular endereço IP ao soquete

Na abordagem antiga usando HttpWebRequest, você pode ter usado lógica personalizada para vincular um endereço IP específico a um soquete. Veja como você pode obter funcionalidade semelhante usando HttpClient e ConnectCallback:

Código antigo usando 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();

Novo código usando HttpClient e 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);

Exemplo: Aplicar opções de soquete específicas

Se você precisar aplicar opções específicas de soquete, como habilitar o TCP keep-alive, poderá usar ConnectCallback para configurar o soquete antes que ele seja usado pelo HttpClient. Na verdade, ConnectCallback é mais flexível para configurar opções de soquete.

Código antigo usando 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();

Novo código usando HttpClient e 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);

Exemplo: Ativar round robin DNS

DNS Round Robin é uma técnica usada para distribuir o tráfego de rede entre vários servidores, girando através de uma lista de endereços IP associados a um único nome de domínio. Isso ajuda no balanceamento de carga e na melhoria da disponibilidade dos serviços. Ao usar HttpClient, você pode implementar o DNS Round Robin manipulando manualmente a resolução DNS e girando pelos endereços IP usando a propriedade ConnectCallback de SocketsHttpHandler.

Para habilitar o DNS Round Robin com HttpClient, você pode usar a propriedade ConnectCallback para resolver manualmente as entradas DNS e girar pelos endereços IP. Aqui está um exemplo para HttpWebRequest e HttpClient:

Código antigo usando HttpWebRequest:

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

Na API mais antiga HttpWebRequest , habilitar o DNS Round Robin era simples devido ao seu suporte interno para esse recurso. No entanto, a API mais recente HttpClient não fornece a mesma funcionalidade interna. Apesar disso, você pode obter um comportamento semelhante implementando um DnsRoundRobinConnector que gira manualmente através dos endereços IP retornados pela resolução DNS.

Novo código usando 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

Você pode encontrar a implementação de DnsRoundRobinConnector aqui.

DnsRoundRobinConnector Utilização:

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

Exemplo: Definir propriedades SocketsHttpHandler

SocketsHttpHandler é um manipulador poderoso e flexível no .NET que fornece opções de configuração avançadas para gerenciar conexões HTTP. Ao definir várias propriedades de SocketsHttpHandler, você pode ajustar o comportamento do seu cliente HTTP para atender a requisitos específicos, como otimização de desempenho, aprimoramentos de segurança e manipulação de conexão personalizada.

Aqui está um exemplo de como configurar SocketsHttpHandler com várias propriedades e usá-lo com 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);

Exemplo: Change ImpersonationLevel

Esta funcionalidade é específica para determinadas plataformas e está um pouco desatualizada. Se você precisar de uma solução alternativa, você pode consultar esta seção do código.

Ao trabalhar com HttpCliento , talvez seja necessário manipular certificados de cliente para vários fins, como validação personalizada de certificados de servidor ou busca de certificado de servidor. HttpClient Fornece várias propriedades e opções para gerenciar certificados de forma eficaz.

Exemplo: Verifique a lista de revogação de certificados com SocketsHttpHandler

A CheckCertificateRevocationList propriedade in SocketsHttpHandler.SslOptions permite que os desenvolvedores habilitem ou desabilitem a verificação de listas de revogação de certificados (CRL) durante o handshake SSL/TLS. Habilitar essa propriedade garante que o cliente verifique se o certificado do servidor foi revogado, aumentando a segurança da conexão.

Código antigo usando HttpWebRequest:

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

Novo código usando 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);

Exemplo: Buscar certificado

Para buscar o certificado do RemoteCertificateValidationCallback in HttpClient, você pode usar a ServerCertificateCustomValidationCallback propriedade de HttpClientHandler ou SocketsHttpHandler.SslOptions. Esse retorno de chamada permite que você inspecione o certificado do servidor durante o handshake SSL/TLS.

Código antigo usando HttpWebRequest:

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

Novo código usando 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");

Exemplo: Habilitar autenticação mútua

A autenticação mútua, também conhecida como SSL bidirecional ou autenticação de certificado de cliente, é um processo de segurança no qual o cliente e o servidor se autenticam. Isso garante que ambas as partes sejam quem afirmam ser, fornecendo uma camada adicional de segurança para comunicações confidenciais. No HttpClient, você pode habilitar a autenticação mútua configurando o HttpClientHandler ou SocketsHttpHandler para incluir o certificado do cliente e validar o certificado do servidor.

Para ativar a autenticação mútua, siga estes passos:

  • Carregue o certificado do cliente.
  • Configure o HttpClientHandler ou SocketsHttpHandler para incluir o certificado do cliente.
  • Configure o retorno de chamada de validação de certificado do servidor se a validação personalizada for necessária.

Aqui está um exemplo usando 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);

Uso de propriedades de cabeçalho

Os cabeçalhos desempenham um papel crucial na comunicação HTTP, fornecendo metadados essenciais sobre a solicitação e a resposta. Ao trabalhar com HttpClient no .NET, você pode definir e gerenciar várias propriedades de cabeçalho para controlar o comportamento de suas solicitações e respostas HTTP. Compreender como usar essas propriedades de cabeçalho de forma eficaz pode ajudá-lo a garantir que seu aplicativo se comunique de forma eficiente e segura com serviços Web.

Definir cabeçalhos de solicitação

Os cabeçalhos de solicitação são usados para fornecer informações adicionais ao servidor sobre a solicitação que está sendo feita. Os casos de uso comuns incluem a especificação do tipo de conteúdo, a configuração de tokens de autenticação e a adição de cabeçalhos personalizados. Você pode definir cabeçalhos de solicitação usando a DefaultRequestHeaders propriedade de HttpClient ou a propriedade Headers de HttpRequestMessage.

Exemplo: definir cabeçalhos de solicitação personalizados

Definindo cabeçalhos de solicitação personalizados padrão no HttpClient

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

Definindo cabeçalhos de solicitação personalizados em HttpRequestMessage

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

Exemplo: definir cabeçalhos de solicitação comuns

Ao trabalhar com HttpRequestMessage no .NET, definir cabeçalhos de solicitação comuns é essencial para fornecer informações adicionais ao servidor sobre a solicitação que está sendo feita. Esses cabeçalhos podem incluir tokens de autenticação e muito mais. Configurar corretamente esses cabeçalhos garante que suas solicitações HTTP sejam processadas corretamente pelo servidor. Para obter uma lista abrangente das propriedades comuns disponíveis no HttpRequestHeaders, consulte Propriedades.

Para definir cabeçalhos de solicitação comuns no HttpRequestMessage, você pode usar a Headers HttpRequestMessage propriedade do objeto. Esta propriedade fornece acesso à HttpRequestHeaders coleção, onde você pode adicionar ou modificar cabeçalhos conforme necessário.

Definindo cabeçalhos de solicitação padrão comuns em HttpClient

using System.Net.Http.Headers;

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

Definindo cabeçalhos de solicitação comuns em HttpRequestMessage

using System.Net.Http.Headers;

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

Exemplo: definir cabeçalhos de conteúdo

Os cabeçalhos de conteúdo são usados para fornecer informações adicionais sobre o corpo de uma solicitação ou resposta HTTP. Ao trabalhar com HttpClient no .NET, você pode definir cabeçalhos de conteúdo para especificar o tipo de mídia, codificação e outros metadados relacionados ao conteúdo que está sendo enviado ou recebido. A configuração correta dos cabeçalhos de conteúdo garante que o servidor e o cliente possam interpretar e processar corretamente o conteúdo.

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

Exemplo: Definir MaximumErrorResponseLength em HttpClient

O MaximumErrorResponseLength uso permite que os desenvolvedores especifiquem o comprimento máximo do conteúdo da resposta de erro que o manipulador armazenará em buffer. Isso é útil para controlar a quantidade de dados que são lidos e armazenados na memória quando uma resposta de erro é recebida do servidor. Usando essa técnica, você pode evitar o uso excessivo de memória e melhorar o desempenho do seu aplicativo ao lidar com grandes respostas de erro.

Há algumas maneiras de fazer isso, vamos examinar TruncatedReadStream a técnica neste exemplo:

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

E exemplo de uso de 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);
}

Exemplo: Aplicar cabeçalhos CachePolicy

Aviso

HttpClient não tem lógica interna para armazenar respostas em cache. Não há outra solução alternativa além de implementar todo o cache por conta própria. Simplesmente definir os cabeçalhos não alcançará o cache.

Ao migrar de HttpWebRequest para o HttpClient, é importante lidar corretamente com cabeçalhos relacionados ao cache, como pragma e cache-control. Esses cabeçalhos controlam como as respostas são armazenadas em cache e recuperadas, garantindo que seu aplicativo se comporte conforme o esperado em termos de desempenho e atualização de dados.

No HttpWebRequest, você pode ter usado a CachePolicy propriedade para definir esses cabeçalhos. No entanto, no HttpClient, você precisa definir manualmente esses cabeçalhos na solicitação.

Código antigo usando HttpWebRequest:

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

Na API mais antiga HttpWebRequest , a aplicação CachePolicy era simples devido ao seu suporte interno para esse recurso. No entanto, a API mais recente HttpClient não fornece a mesma funcionalidade interna. Apesar disso, você pode obter um comportamento semelhante implementando um que adiciona manualmente cabeçalhos AddCacheControlHeaders relacionados ao cache.

Novo código usando HttpClient:

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

Você pode encontrar a implementação de AddCacheControlHeaders aqui.

AddCacheControlHeaders Utilização:

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

Uso de propriedades de buffer

Ao migrar de HttpWebRequest para HttpCliento , é importante entender as diferenças em como essas duas APIs lidam com o buffer.

Código antigo usando HttpWebRequest:

No HttpWebRequest, você tem controle direto sobre as propriedades de buffer por meio das AllowWriteStreamBuffering propriedades e AllowReadStreamBuffering . Essas propriedades habilitam ou desabilitam o buffer de dados enviados e recebidos do servidor.

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

Novo código usando HttpClient:

Na HttpClient, não há equivalentes diretos para as AllowWriteStreamBuffering propriedades e AllowReadStreamBuffering .

HttpClient não buffer corpos de solicitação por conta própria, em vez disso, delegando a responsabilidade para o HttpContent usado. Conteúdos como StringContent ou ByteArrayContent logicamente já estão armazenados em buffer na memória, durante o uso StreamContent não incorrerá em nenhum buffer por padrão. Para forçar o conteúdo a ser armazenado em buffer, você pode ligar HttpContent.LoadIntoBufferAsync antes de enviar a solicitação. Eis um exemplo:

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

No HttpClient buffer de leitura está habilitado por padrão. Para evitá-lo, você pode especificar o HttpCompletionOption.ResponseHeadersRead sinalizador ou usar o GetStreamAsync auxiliar.

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