Guia de migração do HttpWebRequest para o HttpClient
Este artigo tem como objetivo orientar os desenvolvedores no processo de migração do HttpWebRequest, ServicePoint e ServicePointManager para HttpClient. A migração é necessária devido à obsolescência das APIs mais antigas e aos inúmeros benefícios oferecidos pelo 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 do HttpWebRequest
, ServicePoint
e ServicePointManager
para HttpClient
não é apenas uma melhoria de desempenho "boa de se ter". É crucial entender que o desempenho da lógica WebRequest
existente provavelmente será degradado significativamente quando você mudar para o .NET (Core). Isso acontece porque WebRequest
é mantido como uma camada de compatibilidade mínima, o que significa que carece de muitas otimizações, como reutilização de conexão em vários casos. Portanto, a transição para HttpClient
é essencial para garantir que o desempenho e o gerenciamento de recursos do seu aplicativo estejam de acordo com os padrões modernos.
Migrar de HttpWebRequest para HttpClient
Vamos começar com alguns exemplos:
Solicitação GET simples usando HttpWebRequest
Aqui está um exemplo de como o código pode ser:
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 ser:
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 ser:
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 ser:
HttpClient client = new();
using HttpResponseMessage responseMessage = await client.PostAsync(uri, new StringContent("Hello World!"));
Guia de migração do HttpWebRequest para HttpClient, SocketsHttpHandler
Migrar o uso do ServicePoint(Manager)
Você deve estar ciente de que ServicePointManager
é uma classe estática, o que significa que quaisquer feitas em suas propriedades terão um efeito global em todos os objetos ServicePoint
recém-criados 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 nenhuma configuração definida no ServicePointManager
.
Mapeamento das propriedades ServicePointManager
Aviso
No .NET moderno, os valores padrão para as propriedades UseNagleAlgorithm
e Expect100Continue
são definidos como false
. Esses valores eram true
por padrão no .NET Framework.
Mapeamento do método ServicePointManager
ServicePointManagerAPI antigo | Nova API | Observações |
---|---|---|
FindServicePoint |
Não há nenhuma API equivalente. | Sem solução alternativa |
SetTcpKeepAlive |
Nenhuma API equivalente direta. | Uso de SocketsHttpHandler e ConnectCallback. |
Mapeamento das propriedades ServicePoint
ServicePointAPI antigo | Nova API | Observações |
---|---|---|
Address |
HttpRequestMessage.RequestUri |
Este é o uri de solicitação, essas informações podem ser encontradas 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 |
Não há 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 |
Não há nenhuma API equivalente. | Sem solução alternativa |
CurrentConnections |
Não há nenhuma API equivalente. | Consulte Telemetria de rede no .NET. |
Expect100Continue |
ExpectContinue | Exemplo: Definir cabeçalhos de solicitação. |
IdleSince |
Não há 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 |
Não há nenhuma API equivalente. | HttpClient não suporta pipelining. |
UseNagleAlgorithm |
Nenhuma API equivalente direta. | Uso de SocketsHttpHandler e ConnectCallback. |
Mapeamento do método ServicePoint
ServicePointAPI antigo | Nova API | Observações |
---|---|---|
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. Entender 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 os serviços 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 o 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 propriedade ConnectCallback
em 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. Usando ConnectCallback
, você pode interceptar e modificar o processo de conexão antes que ele seja usado pelo HttpClient
.
Exemplo: vincular o endereço IP ao soquete
Na abordagem antiga, usando HttpWebRequest
pode ter usado lógica personalizada para vincular um endereço IP específico a um soquete. Veja como você pode obter uma 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 de soquete específicas, 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 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: Habilitar round robin do DNS
DNS Round Robin é uma técnica usada para distribuir o tráfego de rede em vários servidores, alternando entre 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 alternar entre os endereços IP. Este é 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 HttpWebRequest
mais antiga, habilitar o DNS Round Robin era simples devido ao suporte integrado a esse recurso. No entanto, a API HttpClient
mais recente não fornece a mesma funcionalidade interna. Apesar disso, você pode obter um comportamento semelhante implementando um DnsRoundRobinConnector
que rotaciona manualmente os 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
Uso:
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 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: Alterar ImpersonationLevel
Essa funcionalidade é específica para determinadas plataformas e está um pouco desatualizada. Se você precisar de uma solução alternativa, consulte esta seção do código.
Uso de certificado e propriedades associadas a TLS em HttpClient
Ao trabalhar com HttpClient
, pode ser necessário manipular certificados de cliente para várias finalidades, como validação personalizada de certificados de servidor ou busca do certificado de servidor. HttpClient
fornece várias propriedades e opções para gerenciar certificados de forma eficaz.
Exemplo: verificar a lista de revogação de certificados com SocketsHttpHandler
A propriedade CheckCertificateRevocationList
em 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 a partir do RemoteCertificateValidationCallback
em HttpClient
, você pode usar a propriedade ServerCertificateCustomValidationCallback
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 do 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 ativar a autenticação mútua configurando o HttpClientHandler
ou SocketsHttpHandler
para incluir o certificado do cliente e validar o certificado do servidor.
Para habilitar a autenticação mútua, siga estas etapas:
- Carregue o certificado do cliente.
- Configure o HttpClientHandler ou SocketsHttpHandler para incluir o certificado do cliente.
- Configure o retorno de chamada de validação do certificado do servidor se a validação personalizada for necessária.
Veja 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. Entender 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 os serviços Web.
Configurar cabeçalhos das solicitações
Os cabeçalhos de solicitação são usados para fornecer informações adicionais ao servidor sobre a solicitação que está sendo feita. Casos de uso comuns incluem especificar o tipo de conteúdo, definir tokens de autenticação e adicionar cabeçalhos personalizados. Você pode definir cabeçalhos de solicitação usando a propriedade DefaultRequestHeaders
de HttpClient
ou a propriedade de cabeçalhos de HttpRequestMessage
.
Exemplo: Definir os cabeçalhos de solicitação personalizados
Definindo cabeçalhos de solicitação personalizados padrão em 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 os cabeçalhos de solicitação comuns
Ao trabalhar com HttpRequestMessage
no .NET, a configuração de 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. A configuração adequada desses cabeçalhos garante que suas solicitações HTTP sejam processadas corretamente pelo servidor.
Para ver 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 propriedade Headers
do objeto HttpRequestMessage
. Essa propriedade fornece acesso à coleção HttpRequestHeaders
, 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, a codificação e outros metadados relacionados ao conteúdo que está sendo enviado ou recebido. A configuração adequada de 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 uso de MaximumErrorResponseLength
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 aplicativo ao lidar com respostas de erro grandes.
Existem algumas maneiras de fazer isso, examinaremos a técnica TruncatedReadStream
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 de CachePolicy
Aviso
O HttpClient
não tem lógica interna para armazenar respostas em cache. Não há 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 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 propriedade CachePolicy
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 HttpWebRequest
mais antiga, a aplicação de CachePolicy
era simples devido ao suporte integrado para esse recurso. No entanto, a API HttpClient
mais recente não fornece a mesma funcionalidade interna. Apesar disso, você pode obter um comportamento semelhante implementando um AddCacheControlHeaders
que adiciona manualmente cabeçalhos 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
Uso:
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 HttpClient
, é 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 propriedades AllowWriteStreamBuffering
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
:
Em HttpClient
, não há equivalentes diretos para as propriedades AllowWriteStreamBuffering
e AllowReadStreamBuffering
.
HttpClient não armazena corpos de solicitação em buffer por conta própria, em vez disso, delega a responsabilidade ao HttpContent
usado. Conteúdos como StringContent
ou ByteArrayContent
são logicamente já armazenados em buffer na memória, enquanto o uso de StreamContent
não incorrerá em nenhum buffer por padrão. Para forçar o conteúdo a ser armazenado em buffer, você pode chamar HttpContent.LoadIntoBufferAsync
antes de enviar a solicitação. Veja 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);
Em HttpClient
o buffer de leitura é habilitado por padrão. Para evitá-lo, você pode especificar o sinalizador HttpCompletionOption.ResponseHeadersRead
ou usar o auxiliar 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);