Compartir a través de


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

API anterior de HttpWebRequest Nueva API Notas
Accept Accept Ejemplo: Establecimiento de encabezados de solicitud.
Address RequestUri Ejemplo: Captura del URI redirigido.
AllowAutoRedirect AllowAutoRedirect Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
AllowReadStreamBuffering No hay API equivalente directa. Uso de propiedades de almacenamiento en búfer.
AllowWriteStreamBuffering No hay API equivalente directa. Uso de propiedades de almacenamiento en búfer.
AuthenticationLevel No hay API equivalente directa. Ejemplo: Habilitación de la autenticación mutua.
AutomaticDecompression AutomaticDecompression Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
CachePolicy No hay API equivalente directa. Ejemplo: Aplicación de encabezados CachePolicy.
ClientCertificates SslOptions.ClientCertificates Uso de propiedades relacionadas con certificados en HttpClient.
Connection Connection Ejemplo: Establecimiento de encabezados de solicitud.
ConnectionGroupName Sin API equivalente Ninguna solución alternativa
ContentLength ContentLength Ejemplo: Establecimiento de encabezados de contenido.
ContentType ContentType Ejemplo: Establecimiento de encabezados de contenido.
ContinueDelegate Sin API equivalente Ninguna solución alternativa.
ContinueTimeout Expect100ContinueTimeout Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
CookieContainer CookieContainer Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
Credentials Credentials Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
Date Date Ejemplo: Establecimiento de encabezados de solicitud.
DefaultCachePolicy No hay API equivalente directa. Ejemplo: Aplicación de encabezados CachePolicy.
DefaultMaximumErrorResponseLength No hay API equivalente directa. Ejemplo: Establecimiento de MaximumErrorResponseLength en HttpClient.
DefaultMaximumResponseHeadersLength Sin API equivalente MaxResponseHeadersLength se puede usar en su lugar.
DefaultWebProxy Sin API equivalente Proxy se puede usar en su lugar.
Expect Expect Ejemplo: Establecimiento de encabezados de solicitud.
HaveResponse Sin API equivalente Implícito al tener una instancia de HttpResponseMessage.
Headers Headers Ejemplo: Establecimiento de encabezados de solicitud.
Host Host Ejemplo: Establecimiento de encabezados de solicitud.
IfModifiedSince IfModifiedSince Ejemplo: Establecimiento de encabezados de solicitud.
ImpersonationLevel No hay API equivalente directa. Ejemplo: Cambio de ImpersonationLevel.
KeepAlive No hay API equivalente directa. Ejemplo: Establecimiento de encabezados de solicitud.
MaximumAutomaticRedirections MaxAutomaticRedirections Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
MaximumResponseHeadersLength MaxResponseHeadersLength Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
MediaType No hay API equivalente directa. Ejemplo: Establecimiento de encabezados de contenido.
Method Method Ejemplo: Uso de las propiedades de HttpRequestMessage.
Pipelined Sin API equivalente HttpClient no admite la canalización.
PreAuthenticate PreAuthenticate
ProtocolVersion HttpRequestMessage.Version Ejemplo: Uso de las propiedades de HttpRequestMessage.
Proxy Proxy Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
ReadWriteTimeout No hay API equivalente directa. Uso de SocketsHttpHandler y ConnectCallback.
Referer Referrer Ejemplo: Establecimiento de encabezados de solicitud.
RequestUri RequestUri Ejemplo: Uso de las propiedades de HttpRequestMessage.
SendChunked TransferEncodingChunked Ejemplo: Establecimiento de encabezados de solicitud.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
ServicePoint Sin API equivalente ServicePoint no forma parte de HttpClient.
SupportsCookieContainer Sin API equivalente Siempre es true para HttpClient.
Timeout Timeout
TransferEncoding TransferEncoding Ejemplo: Establecimiento de encabezados de solicitud.
UnsafeAuthenticatedConnectionSharing Sin API equivalente Ninguna solución alternativa
UseDefaultCredentials No hay API equivalente directa. Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
UserAgent UserAgent Ejemplo: Establecimiento de encabezados de solicitud.

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

API anterior de ServicePointManager Nueva API Notas
CheckCertificateRevocationList SslOptions.CertificateRevocationCheckMode Ejemplo: Habilitación de la comprobación de CRL con SocketsHttpHandler.
DefaultConnectionLimit MaxConnectionsPerServer Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
DnsRefreshTimeout Sin API equivalente Ejemplo: Habilitación de Round Robin Dns.
EnableDnsRoundRobin Sin API equivalente Ejemplo: Habilitación de Round Robin Dns.
EncryptionPolicy SslOptions.EncryptionPolicy Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
Expect100Continue ExpectContinue Ejemplo: Establecimiento de encabezados de solicitud.
MaxServicePointIdleTime PooledConnectionIdleTimeout Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
MaxServicePoints Sin API equivalente ServicePoint no forma parte de HttpClient.
ReusePort No hay API equivalente directa. Uso de SocketsHttpHandler y ConnectCallback.
SecurityProtocol SslOptions.EnabledSslProtocols Ejemplo: Establecimiento de las propiedades de SocketsHttpHandler.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Ambas son RemoteCertificateValidationCallback
UseNagleAlgorithm No hay API equivalente directa. Uso de SocketsHttpHandler y ConnectCallback.

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.

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