Partager via


Guide de migration de HttpWebRequest vers HttpClient

Cet article vise à guider les développeurs dans le processus de migration de HttpWebRequest, ServicePoint et ServicePointManager vers HttpClient. La migration est nécessaire en raison de l’obsolescence des anciennes API et des nombreux avantages offerts par HttpClient, notamment une amélioration des performances, une meilleure gestion des ressources et une conception d’API plus moderne et flexible. En suivant les étapes décrites dans ce document, les développeurs pourront faire la transition de leurs bases de code en douceur et tirer pleinement parti des fonctionnalités fournies par HttpClient.

Avertissement

La migration de HttpWebRequest, ServicePoint et ServicePointManager vers HttpClient n’est pas seulement une amélioration de performances « souhaitable ». Il est essentiel de comprendre que les performances de la logique WebRequest existante risquent de se dégrader considérablement lorsque vous passerez à .NET (Core). En effet, WebRequest est maintenu comme une couche de compatibilité minimale, ce qui signifie qu'il manque de nombreuses optimisations, telles que la réutilisation des connexions dans de nombreux cas. Par conséquent, passer à HttpClient est essentiel pour garantir que les performances de votre application et la gestion des ressources soient conformes aux normes modernes.

Migrer de HttpWebRequest vers HttpClient

Commençons par quelques exemples :

Simple requête GET utilisant HttpWebRequest

Voici un exemple de ce à quoi le code pourrait ressembler :

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

Simple requête GET utilisant HttpClient

Voici un exemple de ce à quoi le code pourrait ressembler :

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

Simple requête POST utilisant HttpWebRequest

Voici un exemple de ce à quoi le code pourrait ressembler :

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

Simple requête POST utilisant HttpClient

Voici un exemple de ce à quoi le code pourrait ressembler :

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

Guide de migration de HttpWebRequest vers HttpClient, SocketsHttpHandler

HttpWebRequest Ancien API Nouvelle API Notes
Accept Accept Exemple : Définir les en-têtes de la requête.
Address RequestUri Exemple : Récupérer l'URI redirigé.
AllowAutoRedirect AllowAutoRedirect Exemple : Définition des propriétés de SocketsHttpHandler.
AllowReadStreamBuffering Pas d’API équivalente directe Utilisation des propriétés de mise en mémoire tampon.
AllowWriteStreamBuffering Pas d’API équivalente directe Utilisation des propriétés de mise en mémoire tampon.
AuthenticationLevel Pas d’API équivalente directe Exemple : Activation de l'authentification mutuelle.
AutomaticDecompression AutomaticDecompression Exemple : Définition des propriétés de SocketsHttpHandler.
CachePolicy Pas d’API équivalente directe Exemple : Appliquer les en-têtes CachePolicy.
ClientCertificates SslOptions.ClientCertificates Utilisation des propriétés liées aux certificats dans HttpClient.
Connection Connection Exemple : Définir les en-têtes de la requête.
ConnectionGroupName Pas d’API équivalente Aucune solution de contournement
ContentLength ContentLength Exemple : Définir les en-têtes de contenu.
ContentType ContentType Exemple : Définir les en-têtes de contenu.
ContinueDelegate Pas d’API équivalente Aucune solution de contournement.
ContinueTimeout Expect100ContinueTimeout Exemple : Définir les propriétés de SocketsHttpHandler : Définition des propriétés de SocketsHttpHandler.
CookieContainer CookieContainer Exemple : Définir les propriétés de SocketsHttpHandler : Définition des propriétés de SocketsHttpHandler.
Credentials Credentials Exemple : Définir les propriétés de SocketsHttpHandler : Définition des propriétés de SocketsHttpHandler.
Date Date Exemple : Définir les en-têtes de la requête.
DefaultCachePolicy Pas d’API équivalente directe Exemple : Appliquer les en-têtes CachePolicy.
DefaultMaximumErrorResponseLength Pas d’API équivalente directe Exemple : Définir les propriétés de SocketsHttpHandler : Définir MaximumErrorResponseLength dans HttpClient.
DefaultMaximumResponseHeadersLength Pas d’API équivalente MaxResponseHeadersLength peut être utilisé à la place.
DefaultWebProxy Pas d’API équivalente Proxy peut être utilisé à la place.
Expect Expect Exemple : Définir les en-têtes de la requête.
HaveResponse Pas d’API équivalente Implémenté en ayant une instance de HttpResponseMessage.
Headers Headers Exemple : Définir les en-têtes de requête : Définir les en-têtes de requête.
Host Host Exemple : Définir les en-têtes de la requête.
IfModifiedSince IfModifiedSince Exemple : Définir les en-têtes de la requête.
ImpersonationLevel Pas d’API équivalente directe Exemple : Modifier le niveau d'identification (ImpersonationLevel).
KeepAlive Pas d’API équivalente directe Exemple : Définir les en-têtes de requête : Définir les en-têtes de requête.
MaximumAutomaticRedirections MaxAutomaticRedirections Exemple : Définition des propriétés de SocketsHttpHandler.
MaximumResponseHeadersLength MaxResponseHeadersLength Exemple : Définition des propriétés de SocketsHttpHandler.
MediaType Pas d’API équivalente directe Exemple : Définir les en-têtes de contenu.
Method Method Exemple : utilisation des propriétés de HttpRequestMessage : Utilisation des propriétés HttpRequestMessage.
Pipelined Pas d’API équivalente HttpClient ne supporte pas le pipelining.
PreAuthenticate PreAuthenticate
ProtocolVersion HttpRequestMessage.Version Exemple : utilisation des propriétés de HttpRequestMessage : Utilisation des propriétés HttpRequestMessage.
Proxy Proxy Exemple : Définition des propriétés de SocketsHttpHandler.
ReadWriteTimeout Pas d’API équivalente directe Utilisation de SocketsHttpHandler et ConnectCallback.
Referer Referrer Exemple : Définir les en-têtes de la requête.
RequestUri RequestUri Exemple : utilisation des propriétés de HttpRequestMessage : Utilisation des propriétés HttpRequestMessage.
SendChunked TransferEncodingChunked Exemple : Définir les en-têtes de la requête.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Exemple : Définition des propriétés de SocketsHttpHandler.
ServicePoint Pas d’API équivalente ServicePoint ne fait pas partie de HttpClient.
SupportsCookieContainer Pas d’API équivalente C’est toujours true pour HttpClient.
Timeout Timeout
TransferEncoding TransferEncoding Exemple : Définir les en-têtes de la requête.
UnsafeAuthenticatedConnectionSharing Pas d’API équivalente Aucune solution de contournement
UseDefaultCredentials Pas d’API équivalente directe Exemple : Définition des propriétés de SocketsHttpHandler.
UserAgent UserAgent Exemple : Définir les en-têtes de la requête.

Migrer l'utilisation du ServicePoint(Manager)

Vous devez savoir que ServicePointManager est une classe statique, ce qui signifie que toute modification apportée à ses propriétés aura un effet global sur tous les objets ServicePoint nouvellement créés au sein de l'application. Par exemple, lorsque vous modifiez une propriété telle que ConnectionLimit ou Expect100Continue, cela a un impact sur chaque nouvelle instance de ServicePoint.

Avertissement

Dans .NET moderne, HttpClient ne prend pas en compte les configurations définies sur ServicePointManager.

ServicePointManager mappage des propriétés

ServicePointManager Ancien API Nouvelle API Notes
CheckCertificateRevocationList SslOptions.CertificateRevocationCheckMode Exemple : Activation de la vérification de la CRL avec SocketsHttpHandler.
DefaultConnectionLimit MaxConnectionsPerServer Exemple : Définition des propriétés de SocketsHttpHandler.
DnsRefreshTimeout Pas d’API équivalente Exemple : Activation de Dns Round Robin.
EnableDnsRoundRobin Pas d’API équivalente Exemple : Activation de Dns Round Robin.
EncryptionPolicy SslOptions.EncryptionPolicy Exemple : Définition des propriétés de SocketsHttpHandler.
Expect100Continue ExpectContinue Exemple : Définir les en-têtes de la requête.
MaxServicePointIdleTime PooledConnectionIdleTimeout Exemple : Définition des propriétés de SocketsHttpHandler.
MaxServicePoints Pas d’API équivalente ServicePoint ne fait pas partie de HttpClient.
ReusePort Pas d’API équivalente directe Utilisation de SocketsHttpHandler et ConnectCallback.
SecurityProtocol SslOptions.EnabledSslProtocols Exemple : Définition des propriétés de SocketsHttpHandler.
ServerCertificateValidationCallback SslOptions.RemoteCertificateValidationCallback Les deux sont RemoteCertificateValidationCallback.
UseNagleAlgorithm Pas d’API équivalente directe Utilisation de SocketsHttpHandler et ConnectCallback.

Avertissement

Dans .NET moderne, les valeurs par défaut pour les propriétés UseNagleAlgorithm et Expect100Continue sont définies sur false. Ces valeurs étaient true par défaut dans .NET Framework.

ServicePointManager mappage des méthodes

ServicePointManager Ancien API Nouvelle API Notes
FindServicePoint Pas d’API équivalente Aucune solution de contournement
SetTcpKeepAlive Pas d’API équivalente directe Utilisation de SocketsHttpHandler et ConnectCallback.

ServicePoint mappage des propriétés

ServicePoint Ancien API Nouvelle API Notes
Address HttpRequestMessage.RequestUri Il s’agit de l’URI de la requête, cette information se trouve sous HttpRequestMessage.
BindIPEndPointDelegate Pas d’API équivalente directe Utilisation de SocketsHttpHandler et ConnectCallback.
Certificate Pas d’API équivalente directe Cette information peut être récupérée depuis RemoteCertificateValidationCallback. Exemple : Récupérer un certificat.
ClientCertificate Pas d’API équivalente Exemple : Activation de l'authentification mutuelle.
ConnectionLeaseTimeout SocketsHttpHandler.PooledConnectionLifetime Réglage équivalent dans HttpClient.
ConnectionLimit MaxConnectionsPerServer Exemple : Définition des propriétés de SocketsHttpHandler.
ConnectionName Pas d’API équivalente Aucune solution de contournement
CurrentConnections Pas d’API équivalente Voir Télémétrie réseau en .NET.
Expect100Continue ExpectContinue Exemple : Définir les en-têtes de la requête.
IdleSince Pas d’API équivalente Aucune solution de contournement
MaxIdleTime PooledConnectionIdleTimeout Exemple : Définition des propriétés de SocketsHttpHandler.
ProtocolVersion HttpRequestMessage.Version Exemple : utilisation des propriétés de HttpRequestMessage : Utilisation des propriétés HttpRequestMessage.
ReceiveBufferSize Pas d’API équivalente directe Utilisation de SocketsHttpHandler et ConnectCallback.
SupportsPipelining Pas d’API équivalente HttpClient ne supporte pas le pipelining.
UseNagleAlgorithm Pas d’API équivalente directe Utilisation de SocketsHttpHandler et ConnectCallback.

ServicePoint mappage des méthodes

ServicePoint Ancien API Nouvelle API Notes
CloseConnectionGroup Pas d'équivalent Aucune solution de contournement
SetTcpKeepAlive Pas d’API équivalente directe Utilisation de SocketsHttpHandler et ConnectCallback.

Utilisation des propriétés HttpClient et HttpRequestMessage

Lorsque vous travaillez avec HttpClient dans .NET, vous avez accès à diverses propriétés qui vous permettent de configurer et de personnaliser les requêtes et réponses HTTP. Comprendre ces propriétés peut vous aider à tirer le meilleur parti de HttpClient et à garantir que votre application communique efficacement et en toute sécurité avec les services web.

Exemple : Utilisation des propriétés HttpRequestMessage

Voici un exemple d’utilisation combinée de HttpClient et HttpRequestMessage :

var client = new HttpClient();

var request = new HttpRequestMessage(HttpMethod.Post, "https://example.com"); // Method and RequestUri usage
var request = new HttpRequestMessage() // Alternative way to set RequestUri and Method
{
    RequestUri = new Uri("https://example.com"),
    Method = HttpMethod.Post
};
request.Headers.Add("Custom-Header", "value");
request.Content = new StringContent("somestring");

using var response = await client.SendAsync(request);
var protocolVersion = response.RequestMessage.Version; // Fetch `ProtocolVersion`.

Exemple : Récupérer l'URI redirigé

Voici un exemple de récupération de l’URI redirigé (identique à HttpWebRequest.Address) :

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

Utilisation de SocketsHttpHandler et ConnectCallback

La propriété ConnectCallback dans SocketsHttpHandler permet aux développeurs de personnaliser le processus d’établissement d’une connexion TCP. Cela peut être utile dans les scénarios où vous devez contrôler la résolution DNS ou appliquer des options spécifiques aux sockets lors de la connexion. En utilisant ConnectCallback, vous pouvez intercepter et modifier le processus de connexion avant qu’il ne soit utilisé par HttpClient.

Exemple : Lier l'adresse IP à la socket

Dans l’approche ancienne utilisant HttpWebRequest, vous pourriez avoir utilisé une logique personnalisée pour lier une adresse IP spécifique à un socket. Voici comment vous pouvez obtenir une fonctionnalité similaire en utilisant HttpClient et ConnectCallback :

Ancien code utilisant 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();

Nouveau code utilisant HttpClient et 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);

Exemple : lier l'adresse IP à la socket Appliquer des options de socket spécifiques

Si vous devez appliquer des options spécifiques aux sockets, comme l’activation du keep-alive TCP, vous pouvez utiliser ConnectCallback pour configurer le socket avant qu’il ne soit utilisé par HttpClient. En fait, ConnectCallback est plus flexible pour configurer les options des sockets.

Ancien code utilisant 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();

Nouveau code utilisant HttpClient et 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);

Exemple : Activer le DNS round robin

Le DNS Round Robin est une technique utilisée pour répartir le trafic réseau entre plusieurs serveurs en faisant tourner une liste d’adresses IP associées à un même nom de domaine. Cela aide à équilibrer la charge et à améliorer la disponibilité des services. Lorsque vous utilisez HttpClient, vous pouvez implémenter le DNS Round Robin en gérant manuellement la résolution DNS et en faisant tourner les adresses IP à l’aide de la propriété ConnectCallback de SocketsHttpHandler.

Pour activer le DNS Round Robin avec HttpClient, vous pouvez utiliser la propriété ConnectCallback pour résoudre manuellement les entrées DNS et faire tourner les adresses IP. Voici un exemple pour HttpWebRequest et HttpClient :

Ancien code utilisant HttpWebRequest :

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

Dans l’ancienne API HttpWebRequest, l’activation du DNS Round Robin était simple grâce à la prise en charge intégrée de cette fonctionnalité. Cependant, la nouvelle API HttpClient ne fournit pas la même fonctionnalité intégrée. Néanmoins, vous pouvez obtenir un comportement similaire en implémentant un DnsRoundRobinConnector qui fait tourner manuellement les adresses IP retournées par la résolution DNS.

Nouveau code utilisant 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

Vous pouvez trouver l’implémentation de DnsRoundRobinConnector ici.

DnsRoundRobinConnector Utilisation :

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

Exemple : Définir les propriétés de SocketsHttpHandler

SocketsHttpHandler est un gestionnaire puissant et flexible dans .NET qui offre des options de configuration avancées pour la gestion des connexions HTTP. En définissant diverses propriétés de SocketsHttpHandler, vous pouvez affiner le comportement de votre client HTTP pour répondre à des exigences spécifiques, telles que l’optimisation des performances, les améliorations de la sécurité et la gestion personnalisée des connexions.

Voici un exemple de configuration de SocketsHttpHandler avec diverses propriétés et de son utilisation avec 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);

Exemple : Changer ImpersonationLevel

Cette fonctionnalité est spécifique à certaines plateformes et est quelque peu obsolète. Si vous avez besoin d’une solution de contournement, vous pouvez vous référer à cette section du code.

Lorsque vous travaillez avec HttpClient, vous pourriez avoir besoin de gérer des certificats clients pour diverses raisons, telles que la validation personnalisée des certificats serveur ou la récupération du certificat serveur. HttpClient fournit plusieurs propriétés et options pour gérer efficacement les certificats.

Exemple : Vérification de la liste de révocation des certificats avec SocketsHttpHandler

La propriété CheckCertificateRevocationList dans SocketsHttpHandler.SslOptions permet aux développeurs d’activer ou de désactiver la vérification des listes de révocation de certificats (CRL) lors de la poignée de main SSL/TLS. L’activation de cette propriété garantit que le client vérifie si le certificat du serveur a été révoqué, renforçant ainsi la sécurité de la connexion.

Ancien code utilisant HttpWebRequest :

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

Nouveau code utilisant 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);

Exemple : Récupérer un certificat

Pour récupérer le certificat à partir de RemoteCertificateValidationCallback dans HttpClient, vous pouvez utiliser la propriété ServerCertificateCustomValidationCallback de HttpClientHandler ou SocketsHttpHandler.SslOptions. Ce callback vous permet d’inspecter le certificat du serveur pendant la poignée de main SSL/TLS.

Ancien code utilisant HttpWebRequest :

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

Nouveau code utilisant 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");

Exemple : Activer l'authentification mutuelle

L’authentification mutuelle, également connue sous le nom de SSL à deux voies ou d’authentification par certificat client, est un processus de sécurité dans lequel le client et le serveur s’authentifient mutuellement. Cela garantit que les deux parties sont bien celles qu’elles prétendent être, offrant une couche supplémentaire de sécurité pour les communications sensibles. Dans HttpClient, vous pouvez activer l’authentification mutuelle en configurant HttpClientHandler ou SocketsHttpHandler pour inclure le certificat client et valider le certificat du serveur.

Pour activer l’authentification mutuelle, suivez ces étapes :

  • Charger le certificat client.
  • Configurer HttpClientHandler ou SocketsHttpHandler pour inclure le certificat client.
  • Configurer le callback de validation du certificat serveur si une validation personnalisée est nécessaire.

Voici un exemple utilisant 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);

Utilisation des propriétés des en-têtes

Les en-têtes jouent un rôle crucial dans la communication HTTP, fournissant des métadonnées essentielles sur la requête et la réponse. Lorsque vous travaillez avec HttpClient dans .NET, vous pouvez définir et gérer diverses propriétés d’en-têtes pour contrôler le comportement de vos requêtes et réponses HTTP. Comprendre comment utiliser efficacement ces propriétés d’en-têtes peut vous aider à garantir que votre application communique de manière efficace et sécurisée avec les services web.

Définition des en-têtes de requête

Les en-têtes de requête sont utilisés pour fournir des informations supplémentaires au serveur concernant la requête effectuée. Les cas d’utilisation courants incluent la spécification du type de contenu, la définition de jetons d’authentification et l’ajout d’en-têtes personnalisés. Vous pouvez définir des en-têtes de requête en utilisant la propriété DefaultRequestHeaders de HttpClient ou la propriété Headers de HttpRequestMessage.

Exemple : Définir des en-têtes de requête personnalisés

Définir les en-têtes de requête personnalisés par défaut dans HttpClient

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

Définir les en-têtes de requête personnalisés dans HttpRequestMessage

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

Exemple : Définir des en-têtes de requête personnalisés Définir des en-têtes de requête communs

Lorsque vous travaillez avec HttpRequestMessage dans .NET, la définition d’en-têtes de requête courants est essentielle pour fournir des informations supplémentaires au serveur concernant la requête effectuée. Ces en-têtes peuvent inclure des jetons d’authentification et bien plus encore. Configurer correctement ces en-têtes garantit que vos requêtes HTTP sont traitées correctement par le serveur. Pour obtenir une liste complète des propriétés communes disponibles dans HttpRequestHeaders, consultez la section Propriétés.

Pour définir des en-têtes de requête courants dans HttpRequestMessage, vous pouvez utiliser la propriété Headers de l’objet HttpRequestMessage. Cette propriété donne accès à la collection HttpRequestHeaders, où vous pouvez ajouter ou modifier des en-têtes selon les besoins.

Définir les en-têtes de requête par défaut dans HttpClient

using System.Net.Http.Headers;

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

Définir les en-têtes de requête courants dans HttpRequestMessage

using System.Net.Http.Headers;

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

Exemple : Définir des en-têtes de requête personnalisés Définir des en-têtes de contenu

Les en-têtes de contenu sont utilisés pour fournir des informations supplémentaires sur le corps d’une requête ou d’une réponse HTTP. Lorsque vous travaillez avec HttpClient dans .NET, vous pouvez définir des en-têtes de contenu pour spécifier le type de média, l’encodage et d’autres métadonnées relatives au contenu envoyé ou reçu. Configurer correctement les en-têtes de contenu garantit que le serveur et le client peuvent interpréter et traiter correctement le contenu.

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

Exemple : Définir la longueur maximale de la réponse d’erreur dans HttpClient

L’utilisation de MaximumErrorResponseLength permet aux développeurs de spécifier la longueur maximale du contenu de la réponse d’erreur que le gestionnaire va mettre en mémoire tampon. Cela est utile pour contrôler la quantité de données lues et stockées en mémoire lorsque le serveur renvoie une réponse d’erreur. En utilisant cette technique, vous pouvez éviter une utilisation excessive de la mémoire et améliorer les performances de votre application lors de la gestion de réponses d’erreur volumineuses.

Il existe plusieurs façons de le faire, nous examinerons la technique TruncatedReadStream dans cet exemple :

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

Et l’exemple d’utilisation 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);
}

Exemple : Appliquer les en-têtes CachePolicy Appliquer les en-têtes CachePolicy

Avertissement

HttpClient n'a pas de logique intégrée pour mettre en cache les réponses. Il n’existe pas de solution de contournement autre que d’implémenter tout le mécanisme de mise en cache par vous-même. Simplement définir les en-têtes ne permettra pas de mettre en cache.

Lors de la migration de HttpWebRequest vers HttpClient, il est important de gérer correctement les en-têtes liés au cache, tels que pragma et cache-control. Ces en-têtes contrôlent la manière dont les réponses sont mises en cache et récupérées, garantissant que votre application fonctionne comme prévu en termes de performances et de fraîcheur des données.

Dans HttpWebRequest, vous pourriez avoir utilisé la propriété CachePolicy pour définir ces en-têtes. Cependant, dans HttpClient, vous devez définir manuellement ces en-têtes sur la requête.

Ancien code utilisant HttpWebRequest :

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

Dans l’ancienne API HttpWebRequest, l’application de CachePolicy était simple grâce à sa prise en charge intégrée de cette fonctionnalité. Cependant, la nouvelle API HttpClient ne fournit pas la même fonctionnalité intégrée. Néanmoins, vous pouvez obtenir un comportement similaire en implémentant un AddCacheControlHeaders qui ajoute manuellement les en-têtes liés au cache.

Nouveau code utilisant HttpClient :

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

Vous pouvez trouver l’implémentation de AddCacheControlHeaders ici.

AddCacheControlHeaders Utilisation :

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

Utilisation des propriétés de mise en mémoire tampon.

Lors de la migration de HttpWebRequest vers HttpClient, il est important de comprendre les différences dans la gestion de la mise en mémoire tampon entre ces deux API.

Ancien code utilisant HttpWebRequest :

Dans HttpWebRequest, vous avez un contrôle direct sur les propriétés de mise en mémoire tampon via les propriétés AllowWriteStreamBuffering et AllowReadStreamBuffering. Ces propriétés permettent d’activer ou de désactiver la mise en mémoire tampon des données envoyées au serveur ou reçues du serveur.

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

Nouveau code utilisant HttpClient :

Dans HttpClient, il n’existe pas d’équivalents directs aux propriétés AllowWriteStreamBuffering et AllowReadStreamBuffering.

HttpClient ne met pas en mémoire tampon les corps de requêtes de lui-même, mais délègue cette responsabilité au HttpContent utilisé. Des contenus comme StringContent ou ByteArrayContent sont logiquement déjà mis en mémoire tampon dans la mémoire, tandis que l’utilisation de StreamContent n’engendrera pas de mise en mémoire tampon par défaut. Pour forcer la mise en mémoire tampon du contenu, vous pouvez appeler HttpContent.LoadIntoBufferAsync avant d’envoyer la requête. Voici un exemple :

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

Dans HttpClient, la mise en mémoire tampon en lecture est activée par défaut. Pour l’éviter, vous pouvez spécifier l’indicateur HttpCompletionOption.ResponseHeadersRead ou utiliser l’assistant 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);