Guía de migración de HttpWebRequest a HttpClient
Este artículo pretende guiar a los desarrolladores a través del proceso de migración de HttpWebRequest, ServicePoint y ServicePointManager a HttpClient. La migración es necesaria debido a la obsolescencia de las API anteriores y a las numerosas ventajas que ofrece HttpClient, incluido un rendimiento mejorado, una mejor administración de los recursos y un diseño de API más moderno y flexible. Siguiendo los pasos descritos en este documento, los desarrolladores podrán realizar la transición de sus bases de código sin problemas y aprovechar al máximo las características que proporciona HttpClient.
Advertencia
La migración de HttpWebRequest
, ServicePoint
y ServicePointManager
a HttpClient
no es solo una mejora del rendimiento "agradable". Es fundamental comprender que es probable que el rendimiento de la lógica de WebRequest
existente se degrade significativamente una vez que se pase a .NET (Core). Esto se debe a que WebRequest
se mantiene como una capa de compatibilidad mínima, lo que significa que carece de muchas optimizaciones, como la reutilización de conexiones en numerosos casos. Por lo tanto, cambiar a HttpClient
es esencial para garantizar que el rendimiento y la administración de recursos de su aplicación están a la altura de los estándares modernos.
Migración desde HttpWebRequest a HttpClient
Comencemos con algunos ejemplos:
Solicitud GET simple con HttpWebRequest
He aquí un ejemplo de cómo podría ser el código:
HttpWebRequest request = WebRequest.CreateHttp(uri);
using WebResponse response = await request.GetResponseAsync();
Solicitud GET simple con HttpClient
He aquí un ejemplo de cómo podría ser el código:
HttpClient client = new();
using HttpResponseMessage message = await client.GetAsync(uri);
Solicitud POST simple con HttpWebRequest
He aquí un ejemplo de cómo podría ser el código:
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();
Solicitud POST simple con HttpClient
He aquí un ejemplo de cómo podría ser el código:
HttpClient client = new();
using HttpResponseMessage responseMessage = await client.PostAsync(uri, new StringContent("Hello World!"));
Guía de migración de HttpWebRequest a HttpClient, SocketsHttpHandler
Migración del uso de ServicePoint(Manager)
Debe tener en cuenta que ServicePointManager
es una clase estática, lo que significa que los cambios realizados en sus propiedades tendrán un efecto global en todos los objetos ServicePoint
recién creados dentro de la aplicación. Por ejemplo, modificar una propiedad como ConnectionLimit
o Expect100Continue
afectará a cada nueva instancia de ServicePoint.
Advertencia
En .NET moderno, HttpClient
no tiene en cuenta ninguna configuración establecida en ServicePointManager
.
Asignación de propiedades de ServicePointManager
Advertencia
En .NET moderno, los valores predeterminados de las propiedades UseNagleAlgorithm
y Expect100Continue
se establecen en false
. Estos valores eran true
de forma predeterminada en .NET Framework.
Asignación de método de ServicePointManager
API anterior de ServicePointManager | Nueva API | Notas |
---|---|---|
FindServicePoint |
Sin API equivalente | Ninguna solución alternativa |
SetTcpKeepAlive |
No hay API equivalente directa. | Uso de SocketsHttpHandler y ConnectCallback. |
Asignación de propiedades de ServicePoint
API anterior de ServicePoint | Nueva API | Notas |
---|---|---|
Address |
HttpRequestMessage.RequestUri |
Este es el URI de solicitud, esta información se puede encontrar en HttpRequestMessage . |
BindIPEndPointDelegate |
No hay API equivalente directa. | Uso de SocketsHttpHandler y ConnectCallback. |
Certificate |
No hay API equivalente directa. | Esta información se puede capturar de RemoteCertificateValidationCallback . Ejemplo: Captura de certificado. |
ClientCertificate |
Sin API equivalente | Ejemplo: Habilitación de la autenticación mutua. |
ConnectionLeaseTimeout |
SocketsHttpHandler.PooledConnectionLifetime |
Configuración equivalente en HttpClient |
ConnectionLimit |
MaxConnectionsPerServer | Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler. |
ConnectionName |
Sin API equivalente | Ninguna solución alternativa |
CurrentConnections |
Sin API equivalente | Consulte Telemetría de red de .NET. |
Expect100Continue |
ExpectContinue | Ejemplo: Establecimiento de encabezados de solicitud. |
IdleSince |
Sin API equivalente | Ninguna solución alternativa |
MaxIdleTime |
PooledConnectionIdleTimeout | Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler. |
ProtocolVersion |
HttpRequestMessage.Version |
Ejemplo: Uso de las propiedades de HttpRequestMessage. |
ReceiveBufferSize |
No hay API equivalente directa. | Uso de SocketsHttpHandler y ConnectCallback. |
SupportsPipelining |
Sin API equivalente | HttpClient no admite la canalización. |
UseNagleAlgorithm |
No hay API equivalente directa. | Uso de SocketsHttpHandler y ConnectCallback. |
Asignación de método de ServicePoint
API anterior de ServicePoint | Nueva API | Notas |
---|---|---|
CloseConnectionGroup |
No equivalente | Ninguna solución alternativa |
SetTcpKeepAlive |
No hay API equivalente directa. | Uso de SocketsHttpHandler y ConnectCallback. |
Uso de las propiedades HttpClient y HttpRequestMessage
Al trabajar con HttpClient en .NET, tiene acceso a una variedad de propiedades que permiten configurar y personalizar solicitudes y respuestas HTTP. Comprender estas propiedades puede ayudarle a sacar el máximo partido de HttpClient y asegurarse de que la aplicación se comunica de forma eficaz y segura con los servicios web.
Ejemplo: Uso de las propiedades de HttpRequestMessage
Este es un ejemplo de cómo usar HttpClient y 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`.
Ejemplo: Captura del URI redirigido
Este es un ejemplo de cómo capturar el URI redirigido (igual que HttpWebRequest.Address
):
var client = new HttpClient();
using var response = await client.GetAsync(uri);
var redirectedUri = response.RequestMessage.RequestUri;
Uso de SocketsHttpHandler y ConnectCallback
La propiedad ConnectCallback
de SocketsHttpHandler
permite a los desarrolladores personalizar el proceso de establecimiento de una conexión TCP. Esto puede ser útil para escenarios en los que necesita controlar la resolución DNS o aplicar opciones de socket específicas en la conexión. Al usar ConnectCallback
, puede interceptar y modificar el proceso de conexión antes de que lo use HttpClient
.
Ejemplo: Enlace de la dirección IP al socket
En el enfoque anterior mediante HttpWebRequest
, es posible que haya usado lógica personalizada para enlazar una dirección IP específica a un socket. Aquí se muestra cómo puede lograr una funcionalidad similar mediante HttpClient
y ConnectCallback
:
Código anterior con 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();
Nuevo código con HttpClient
y 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);
Ejemplo: Aplicación de opciones de socket específicas
Si necesita aplicar opciones de socket específicas, como habilitar la conexión persistente deTCP, puede usar ConnectCallback
para configurar el socket antes de que lo use HttpClient
. De hecho, ConnectCallback
es más flexible para configurar las opciones de socket.
Código anterior con 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();
Nuevo código con HttpClient
y 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);
Ejemplo: Habilitación de round robin de DNS
Round Robin DNS es una técnica que se usa para distribuir el tráfico de red entre varios servidores rotando una lista de direcciones IP asociadas a un único nombre de dominio. Esto ayuda a equilibrar la carga y a mejorar la disponibilidad de los servicios. Al usar HttpClient, puede implementar Round Robin DNS controlando manualmente la resolución DNS y rotando las direcciones IP mediante la propiedad ConnectCallback de SocketsHttpHandler.
Para habilitar Round Robin DNS con HttpClient, puede usar la propiedad ConnectCallback para resolver manualmente las entradas DNS y rotar las direcciones IP. Este es un ejemplo para HttpWebRequest
y HttpClient
:
Código anterior con HttpWebRequest
:
ServicePointManager.DnsRefreshTimeout = 60000;
ServicePointManager.EnableDnsRoundRobin = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
En la API HttpWebRequest
anterior, habilitar Round Robin DNS era sencillo debido a su compatibilidad integrada con esta característica. Sin embargo, la API HttpClient
más reciente no proporciona la misma funcionalidad integrada. A pesar de ello, puede lograr un comportamiento similar mediante la implementación de un DnsRoundRobinConnector
que rote manualmente las direcciones IP devueltas por la resolución DNS.
Nuevo código con 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
Puede encontrar la implementación de DnsRoundRobinConnector
aquí.
Uso de 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);
}
Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler
SocketsHttpHandler es un controlador eficaz y flexible en .NET que proporciona opciones de configuración avanzadas para administrar conexiones HTTP. Al establecer varias propiedades de SocketsHttpHandler, puede ajustar el comportamiento del cliente HTTP para satisfacer requisitos específicos, como la optimización del rendimiento, las mejoras de seguridad y el control de conexiones personalizados.
Este es un ejemplo de cómo configurar SocketsHttpHandler con varias propiedades y usarlo con 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);
Ejemplo: Cambio de ImpersonationLevel
Esta funcionalidad es específica de determinadas plataformas y está algo obsoleta. Si necesita una solución alternativa, puede consultar esta sección del código.
Uso de propiedades relacionadas con certificados y TLS en HttpClient
Al trabajar con HttpClient
, es posible que tenga que controlar los certificados de cliente con diversos fines, como la validación personalizada de certificados de servidor o la captura del certificado de servidor. HttpClient
proporciona varias propiedades y opciones para administrar certificados de forma eficaz.
Ejemplo: Comprobación de la lista de revocación de certificados con SocketsHttpHandler
La propiedad CheckCertificateRevocationList
de SocketsHttpHandler.SslOptions
permite a los desarrolladores habilitar o deshabilitar la comprobación de listas de revocación de certificados (CRL) durante el protocolo de enlace SSL/TLS. Habilitar esta propiedad garantiza que el cliente compruebe si se ha revocado el certificado del servidor, lo que mejora la seguridad de la conexión.
Código anterior con HttpWebRequest
:
ServicePointManager.CheckCertificateRevocationList = true;
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Nuevo código con 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);
Ejemplo: Captura de certificado
Para capturar el certificado de RemoteCertificateValidationCallback
en HttpClient
, puede usar la propiedad ServerCertificateCustomValidationCallback
de HttpClientHandler
o SocketsHttpHandler.SslOptions
. Esta devolución de llamada permite inspeccionar el certificado del servidor durante el protocolo de enlace SSL/TLS.
Código anterior con HttpWebRequest
:
HttpWebRequest request = WebRequest.CreateHttp(uri);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
X509Certificate? serverCertificate = request.ServicePoint.Certificate;
Nuevo código con 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");
Ejemplo: Habilitación de la autenticación mutua
La autenticación mutua, también conocida como autenticación SSL o de certificado de cliente bidireccional, es un proceso de seguridad en el que tanto el cliente como el servidor se autentican entre sí. Esto garantiza que ambas partes sean las que dicen ser, lo que proporciona una capa adicional de seguridad para las comunicaciones confidenciales. En HttpClient
, puede habilitar la autenticación mutua configurando HttpClientHandler
o SocketsHttpHandler
para incluir el certificado de cliente y validar el certificado del servidor.
Para habilitar la autenticación mutua, siga estos pasos:
- Cargue el certificado de cliente.
- Configure HttpClientHandler o SocketsHttpHandler para incluir el certificado de cliente.
- Configure la devolución de llamada de validación de certificados de servidor si es necesaria la validación personalizada.
Este es un ejemplo del uso de 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 propiedades de encabezado
Los encabezados desempeñan un papel fundamental en la comunicación HTTP, lo que proporciona metadatos esenciales sobre la solicitud y la respuesta. Al trabajar con HttpClient
en .NET, puede establecer y administrar varias propiedades de encabezado para controlar el comportamiento de las solicitudes y respuestas HTTP. Comprender cómo usar estas propiedades de encabezado de forma eficaz puede ayudarle a garantizar que la aplicación se comunique de forma eficaz y segura con los servicios web.
Establecimiento de encabezados de solicitudes
Los encabezados de solicitud se usan para proporcionar información adicional al servidor sobre la solicitud que se realiza. Entre los casos de uso comunes se incluyen especificar el tipo de contenido, establecer tokens de autenticación y agregar encabezados personalizados. Puede establecer encabezados de solicitud mediante la propiedad DefaultRequestHeaders
de HttpClient
o la propiedad Headers de HttpRequestMessage
.
Ejemplo: Establecimiento de encabezados de solicitud personalizados
Establecimiento de encabezados de solicitud personalizados predeterminados en HttpClient
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Custom-Header", "value");
Establecimiento de encabezados de solicitud personalizados en HttpRequestMessage
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Add("Custom-Header", "value");
Ejemplo: Establecimiento de encabezados de solicitud comunes
Al trabajar con HttpRequestMessage
en .NET, establecer encabezados de solicitud comunes es esencial para proporcionar información adicional al servidor sobre la solicitud que se realiza. Estos encabezados pueden incluir tokens de autenticación y otros muchos elementos. La configuración correcta de estos encabezados garantiza que el servidor procese correctamente las solicitudes HTTP.
Para obtener una lista completa de las propiedades comunes disponibles en HttpRequestHeaders, consulte Propiedades.
Para establecer encabezados de solicitud comunes en HttpRequestMessage
, puede usar la propiedad Headers
del objeto HttpRequestMessage
. Esta propiedad proporciona acceso a la colección HttpRequestHeaders
, donde puede agregar o modificar encabezados según sea necesario.
Establecimiento de encabezados de solicitud comunes predeterminados en HttpClient
using System.Net.Http.Headers;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "token");
Establecimiento de encabezados de solicitud comunes en HttpRequestMessage
using System.Net.Http.Headers;
var request = new HttpRequestMessage(HttpMethod.Get, uri);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token");
Ejemplo: Establecimiento de encabezados de contenido
Los encabezados de contenido se usan para proporcionar información adicional sobre el cuerpo de una solicitud o respuesta HTTP. Al trabajar con HttpClient
en .NET, puede establecer encabezados de contenido para especificar el tipo de contenido multimedia, la codificación y otros metadatos relacionados con el contenido que se envía o recibe. La configuración correcta de los encabezados de contenido garantiza que el servidor y el cliente puedan interpretar y procesar correctamente el contenido.
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);
Ejemplo: Establecimiento de MaximumErrorResponseLength en HttpClient
El uso de MaximumErrorResponseLength
permite a los desarrolladores especificar la longitud máxima del contenido de respuesta de error que el controlador almacenará en búfer. Esto resulta útil para controlar la cantidad de datos que se leen y almacenan en memoria cuando se recibe una respuesta de error del servidor. Con esta técnica, puede evitar un uso excesivo de memoria y mejorar el rendimiento de la aplicación al controlar respuestas de error grandes.
Hay un par de maneras de hacerlo, examinaremos la técnica TruncatedReadStream
en este ejemplo:
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();
}
}
}
Y el ejemplo 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);
}
Ejemplo: Aplicación de encabezados CachePolicy
Advertencia
HttpClient
no tiene lógica integrada para almacenar en caché las respuestas. No hay ninguna solución alternativa que no sea implementar usted mismo todo el almacenamiento en caché. Simplemente establecer los encabezados no logrará el almacenamiento en caché.
Al migrar de HttpWebRequest
a HttpClient
, es importante controlar correctamente los encabezados relacionados con la caché, como pragma
y cache-control
. Estos encabezados controlan cómo se almacenan en caché y recuperan las respuestas, lo que garantiza que la aplicación se comporte según lo previsto en términos de rendimiento y actualización de datos.
En HttpWebRequest
, es posible que haya usado la propiedad CachePolicy
para establecer estos encabezados. Sin embargo, en HttpClient
, debe establecer manualmente estos encabezados en la solicitud.
Código anterior con HttpWebRequest
:
HttpWebRequest request = WebRequest.CreateHttp(uri);
request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
En la API HttpWebRequest
anterior, aplicar CachePolicy
era sencillo gracias a su compatibilidad integrada con esta función. Sin embargo, la API HttpClient
más reciente no proporciona la misma funcionalidad integrada. A pesar de ello, puede lograr un comportamiento similar mediante la implementación de un AddCacheControlHeaders
que agregue manualmente encabezados relacionados con la caché.
Nuevo código con HttpClient
:
public static class CachePolicy
{
public static void AddCacheControlHeaders(HttpRequestMessage request, RequestCachePolicy policy)
Puede encontrar la implementación de AddCacheControlHeaders
aquí.
Uso de 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);
}
Uso de propiedades de almacenamiento en búfer
Al migrar de HttpWebRequest a HttpClient
, es importante comprender las diferencias en el modo en que estas dos API controlan el almacenamiento en búfer.
Código anterior con HttpWebRequest
:
En HttpWebRequest
, tiene control directo sobre las propiedades de almacenamiento en búfer a través de las propiedades AllowWriteStreamBuffering
y AllowReadStreamBuffering
. Estas propiedades habilitan o deshabilitan el almacenamiento en búfer de los datos enviados y recibidos del servidor.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AllowReadStreamBuffering = true; // Default is `false`.
request.AllowWriteStreamBuffering = false; // Default is `true`.
Nuevo código con HttpClient
:
En HttpClient
, no hay equivalentes directos a las propiedades AllowWriteStreamBuffering
y AllowReadStreamBuffering
.
HttpClient no almacena en búfer los cuerpos de las solicitudes por sí mismo, sino que delega la responsabilidad en el HttpContent
usado. El contenido como StringContent
o ByteArrayContent
ya está almacenado en búfer de forma lógica en la memoria, mientras que usar StreamContent
no incurrirá en ningún almacenamiento en búfer de forma predeterminada. Para forzar que el contenido se almacene en búfer, puede llamar a HttpContent.LoadIntoBufferAsync
antes de enviar la solicitud. Este es un ejemplo:
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);
En HttpClient
, el almacenamiento en búfer de lectura está habilitado de manera predeterminada. Para evitarlo, puede especificar la marca HttpCompletionOption.ResponseHeadersRead
o usar el asistente 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);