Effectuer des requêtes HTTP avec la classe HttpClient

Dans cet article, vous allez apprendre à effectuer des requêtes HTTP et à gérer les réponses avec la classe HttpClient.

Important

Tous les exemples de requêtes HTTP ciblent l’une des URL suivantes :

Les points de terminaison HTTP retournent généralement des données JSON (JavaScript Object Notation), mais pas toujours. Par souci de commodité, le package NuGet System.Net.Http.Json facultatif fournit plusieurs méthodes d’extension pour HttpClient et HttpContent qui effectuent la sérialisation automatique et la désérialisation à l’aide de System.Text.Json. Les exemples qui suivent attirent l’attention sur les emplacements où ces extensions sont disponibles.

Conseil

Tout le code source de cet article est disponible dans le référentiel GitHub : .NET Docs.

Créer un HttpClient

La plupart des exemples suivants utilisent la même instance HttpClient et ne doivent donc être configurés qu’une seule fois. Pour créer un HttpClient, utilisez le constructeur de classe HttpClient. Pour plus d’informations, consultez Instructions relatives à l’utilisation de HttpClient.

// HttpClient lifecycle management best practices:
// https://video2.skills-academy.com/dotnet/fundamentals/networking/http/httpclient-guidelines#recommended-use
private static HttpClient sharedClient = new()
{
    BaseAddress = new Uri("https://jsonplaceholder.typicode.com"),
};

Le code précédent :

  • Instancie une nouvelle instance HttpClient en tant que variable static. Conformément aux lignes directrices, il est vivement recommandé de réutiliser les instances HttpClient pendant le cycle de vie de l’application.
  • Définit le HttpClient.BaseAddress sur "https://jsonplaceholder.typicode.com".

Cette instance HttpClient utilise toujours l’adresse de base lors des requêtes suivantes. Pour appliquer une autre configuration, envisagez :

Conseil

Vous pouvez également créer des instances HttpClient à l’aide d’une approche de modèle de fabrique qui vous permet de configurer n’importe quel nombre de clients et de les consommer en tant que services d’injection de dépendances. Pour plus d’informations, consultez Fabrique de client HTTP avec .NET.

Effectuer une requête HTTP

Pour effectuer une requête HTTP, vous appelez l’une des API suivantes :

HTTP method API
GET HttpClient.GetAsync
GET HttpClient.GetByteArrayAsync
GET HttpClient.GetStreamAsync
GET HttpClient.GetStringAsync
POST HttpClient.PostAsync
PUT HttpClient.PutAsync
PATCH HttpClient.PatchAsync
DELETE HttpClient.DeleteAsync
USER SPECIFIED HttpClient.SendAsync

Une requête USER SPECIFIED indique que la méthode SendAsync accepte n’importe quelle valeur HttpMethod valide.

Avertissement

L’envoi de requêtes HTTP est considéré comme un travail lié aux E/S réseau. Bien qu’il existe une méthode HttpClient.Send synchrone, il est recommandé d’utiliser les API asynchrones à la place, sauf si vous avez de bonnes raisons de ne pas le faire.

Remarque

Lors du ciblage d’appareils Android (par exemple, avec le développement MAUI .NET), vous devez ajouter android:usesCleartextTraffic="true" à <application></application> dans AndroidManifest.xml. Cela active le trafic en texte clair, tel que les requêtes HTTP, qui est sinon désactivé par défaut en raison de stratégies de sécurité Android. Par exemple, prenez les paramètres XML suivants :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:usesCleartextTraffic="true"></application>
  <!-- omitted for brevity -->
</manifest>

Pour plus d’informations, veuillez consulter Activation du trafic réseau en texte clair pour le domaine localhost.

Contenu HTTP

Le type HttpContent est utilisé pour représenter un corps d’entité HTTP et les en-têtes de contenu correspondants. Pour les méthodes HTTP (ou méthodes de requête) qui nécessitent un corps, POST, PUTet PATCH, vous utilisez la classe HttpContent pour spécifier le corps de la requête. La plupart des exemples montrent comment préparer la sous-classe StringContent avec une charge utile JSON, mais d’autres sous-classes existent pour différents types de contenu (MIME).

La HttpContent classe est également utilisée pour représenter le corps de la réponse du HttpResponseMessage, accessible sur la propriété HttpResponseMessage.Content.

HTTP Get

Une requête GET ne doit pas envoyer de corps et est utilisée (comme le nom de la méthode l’indique) pour récupérer (ou obtenir) des données d’une ressource. Pour effectuer une requête HTTP GET, en fonction d’un HttpClient et d’un URI, utilisez la méthode HttpClient.GetAsync :

static async Task GetAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.GetAsync("todos/3");
    
    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 3,
    //     "title": "fugiat veniam minus",
    //     "completed": false
    //   }
}

Le code précédent :

  • Effectue une demande GET à "https://jsonplaceholder.typicode.com/todos/3".
  • Garantit que la réponse réussit.
  • Écrit les détails de la demande dans la console.
  • Lit le corps de réponse comme une chaîne.
  • Écrit le corps de la réponse JSON dans la console.

WriteRequestToConsole est une méthode d’extension personnalisée qui ne fait pas partie de l’infrastructure, mais si vous êtes curieux de savoir comment elle est implémentée, considérez le code C# suivant :

static class HttpResponseMessageExtensions
{
    internal static void WriteRequestToConsole(this HttpResponseMessage response)
    {
        if (response is null)
        {
            return;
        }

        var request = response.RequestMessage;
        Console.Write($"{request?.Method} ");
        Console.Write($"{request?.RequestUri} ");
        Console.WriteLine($"HTTP/{request?.Version}");        
    }
}

Cette fonctionnalité est utilisée pour écrire les détails de la demande dans la console sous la forme suivante :

<HTTP Request Method> <Request URI> <HTTP/Version>

Par exemple, la demande GET pour https://jsonplaceholder.typicode.com/todos/3 émet le message suivant :

GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1

HTTP Get à partir de JSON

Le point de terminaison https://jsonplaceholder.typicode.com/todos retourne un tableau JSON d’objets « todo ». La structure JSON se présente de la façon suivante :

[
  {
    "userId": 1,
    "id": 1,
    "title": "example title",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "another example title",
    "completed": true
  },
]

L'objet Todo C# est défini comme suit :

public record class Todo(
    int? UserId = null,
    int? Id = null,
    string? Title = null,
    bool? Completed = null);

Il s’agit d’un type record class, avec des propriétés facultatives Id, Title, Completed et UserId. Pour plus d’informations sur le type record, consultez Présentation des types d’enregistrement en C#. Pour désérialiser automatiquement les requêtes GET en objet C# fortement typé, utilisez la méthode d’extension GetFromJsonAsync qui fait partie du package NuGet System.Net.Http.Json.

static async Task GetFromJsonAsync(HttpClient httpClient)
{
    var todos = await httpClient.GetFromJsonAsync<List<Todo>>(
        "todos?userId=1&completed=false");

    Console.WriteLine("GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1");
    todos?.ForEach(Console.WriteLine);
    Console.WriteLine();

    // Expected output:
    //   GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1
    //   Todo { UserId = 1, Id = 1, Title = delectus aut autem, Completed = False }
    //   Todo { UserId = 1, Id = 2, Title = quis ut nam facilis et officia qui, Completed = False }
    //   Todo { UserId = 1, Id = 3, Title = fugiat veniam minus, Completed = False }
    //   Todo { UserId = 1, Id = 5, Title = laboriosam mollitia et enim quasi adipisci quia provident illum, Completed = False }
    //   Todo { UserId = 1, Id = 6, Title = qui ullam ratione quibusdam voluptatem quia omnis, Completed = False }
    //   Todo { UserId = 1, Id = 7, Title = illo expedita consequatur quia in, Completed = False }
    //   Todo { UserId = 1, Id = 9, Title = molestiae perspiciatis ipsa, Completed = False }
    //   Todo { UserId = 1, Id = 13, Title = et doloremque nulla, Completed = False }
    //   Todo { UserId = 1, Id = 18, Title = dolorum est consequatur ea mollitia in culpa, Completed = False }
}

Dans le code précédent :

  • GETUne demande est adressée à "https://jsonplaceholder.typicode.com/todos?userId=1&completed=false".
    • La chaîne de requête représente les critères de filtrage de la requête.
  • La réponse est automatiquement désérialisée dans un List<Todo> en cas de réussite.
  • Les détails de la demande sont écrits dans la console, ainsi que chaque objet Todo.

Publication HTTP

Une requête POST envoie des données au serveur pour traitement. L’en-tête Content-Type de la demande indique le type MIME que le corps envoie. Pour effectuer une requête POST HTTP, en fonction d’un HttpClient et d’un Uri, utilisez la méthode HttpClient.PostAsync :

static async Task PostAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new
        {
            userId = 77,
            id = 1,
            title = "write code sample",
            completed = false
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PostAsync(
        "todos",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
    //   {
    //     "userId": 77,
    //     "id": 201,
    //     "title": "write code sample",
    //     "completed": false
    //   }
}

Le code précédent :

  • Prépare une instance StringContent avec le corps JSON de la requête (type MIME de "application/json").
  • Adresse une demande POST à "https://jsonplaceholder.typicode.com/todos".
  • Vérifie que la réponse est réussie et écrit les détails de la demande dans la console.
  • Écrit le corps de la réponse sous forme de chaîne dans la console.

Publication HTTP en tant que JSON

Pour sérialiser automatiquement les arguments de requête POST et désérialiser les réponses en objets C# fortement typés, utilisez la méthode d’extension PostAsJsonAsync qui fait partie du package NuGet System.Net.Http.Json.

static async Task PostAsJsonAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.PostAsJsonAsync(
        "todos", 
        new Todo(UserId: 9, Id: 99, Title: "Show extensions", Completed: false));

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var todo = await response.Content.ReadFromJsonAsync<Todo>();
    Console.WriteLine($"{todo}\n");

    // Expected output:
    //   POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
    //   Todo { UserId = 9, Id = 201, Title = Show extensions, Completed = False }
}

Le code précédent :

  • Sérialise l’instance Todo au format JSON et envoie une requête POST à "https://jsonplaceholder.typicode.com/todos".
  • Vérifie que la réponse est réussie et écrit les détails de la demande dans la console.
  • Désérialise le corps de la réponse dans une instance Todo et écrit le Todo dans la console.

Put HTTP

La méthode de requête PUT remplace une ressource existante ou en crée une à l’aide de la charge utile du corps de la requête. Pour effectuer une requête PUT HTTP, en fonction d’un HttpClient et d’un URI, utilisez la méthode HttpClient.PutAsync :

static async Task PutAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new 
        {
            userId = 1,
            id = 1,
            title = "foo bar",
            completed = false
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PutAsync(
        "todos/1",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();
    
    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output:
    //   PUT https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 1,
    //     "title": "foo bar",
    //     "completed": false
    //   }
}

Le code précédent :

  • Prépare une instance StringContent avec le corps JSON de la requête (type MIME de "application/json").
  • Adresse une demande PUT à "https://jsonplaceholder.typicode.com/todos/1".
  • Vérifie que la réponse est réussie et écrit les détails de la demande et un corps de réponse JSON dans la console.

Put HTTP en tant que JSON

Pour sérialiser automatiquement les arguments de requête PUT et désérialiser les réponses en objets C# fortement typés, utilisez la méthode d’extension PutAsJsonAsync qui fait partie du package NuGet System.Net.Http.Json.

static async Task PutAsJsonAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.PutAsJsonAsync(
        "todos/5",
        new Todo(Title: "partially update todo", Completed: true));

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var todo = await response.Content.ReadFromJsonAsync<Todo>();
    Console.WriteLine($"{todo}\n");

    // Expected output:
    //   PUT https://jsonplaceholder.typicode.com/todos/5 HTTP/1.1
    //   Todo { UserId = , Id = 5, Title = partially update todo, Completed = True }
}

Le code précédent :

  • Sérialise l’instance Todo au format JSON et envoie une requête PUT à "https://jsonplaceholder.typicode.com/todos/5".
  • Vérifie que la réponse est réussie et écrit les détails de la demande dans la console.
  • Désérialise le corps de la réponse dans une instance Todo et écrit le Todo dans la console.

Correctif HTTP

La requête PATCH est une mise à jour partielle d’une ressource existante. Elle ne crée pas de ressource et n’est pas destinée à remplacer une ressource existante. Au lieu de cela, il ne met à jour une ressource que partiellement. Pour effectuer une requête PATCH HTTP, en fonction d’un HttpClient et d’un URI, utilisez la méthode HttpClient.PatchAsync :

static async Task PatchAsync(HttpClient httpClient)
{
    using StringContent jsonContent = new(
        JsonSerializer.Serialize(new
        {
            completed = true
        }),
        Encoding.UTF8,
        "application/json");

    using HttpResponseMessage response = await httpClient.PatchAsync(
        "todos/1",
        jsonContent);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output
    //   PATCH https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {
    //     "userId": 1,
    //     "id": 1,
    //     "title": "delectus aut autem",
    //     "completed": true
    //   }
}

Le code précédent :

  • Prépare une instance StringContent avec le corps JSON de la requête (type MIME de "application/json").
  • Adresse une demande PATCH à "https://jsonplaceholder.typicode.com/todos/1".
  • Vérifie que la réponse est réussie et écrit les détails de la demande et un corps de réponse JSON dans la console.

Il n’existe aucune méthode d’extension pour les requêtes PATCH dans le package NuGet System.Net.Http.Json.

Supprimer HTTP

Une requête DELETE supprime une ressource existante. Une requête DELETE est idempotente, mais non sécurisée, ce qui signifie que plusieurs requêtes DELETE adressées aux mêmes ressources produisent le même résultat, mais que la demande affecte l’état de la ressource. Pour effectuer une requête HTTP DELETE, en fonction d’un HttpClient et d’un URI, utilisez la méthode HttpClient.DeleteAsync :

static async Task DeleteAsync(HttpClient httpClient)
{
    using HttpResponseMessage response = await httpClient.DeleteAsync("todos/1");
    
    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    var jsonResponse = await response.Content.ReadAsStringAsync();
    Console.WriteLine($"{jsonResponse}\n");

    // Expected output
    //   DELETE https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
    //   {}
}

Le code précédent :

  • Adresse une demande DELETE à "https://jsonplaceholder.typicode.com/todos/1".
  • Vérifie que la réponse est réussie et écrit les détails de la demande dans la console.

Conseil

La réponse à une requête DELETE (tout comme une requête PUT) peut inclure ou non un corps.

Tête HTTP

La requête HEAD est similaire à une requête GET. Au lieu de retourner la ressource, il retourne uniquement les en-têtes associés à la ressource. Une réponse à la requête HEAD ne retourne pas de corps. Pour effectuer une requête HEAD HTTP, en fonction d’un HttpClient et d’un URI, utilisez la méthode HttpClient.SendAsync avec la HttpMethod défini sur HttpMethod.Head :

static async Task HeadAsync(HttpClient httpClient)
{
    using HttpRequestMessage request = new(
        HttpMethod.Head, 
        "https://www.example.com");

    using HttpResponseMessage response = await httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    foreach (var header in response.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    Console.WriteLine();

    // Expected output:
    //   HEAD https://www.example.com/ HTTP/1.1
    //   Accept-Ranges: bytes
    //   Age: 550374
    //   Cache-Control: max-age=604800
    //   Date: Wed, 10 Aug 2022 17:24:55 GMT
    //   ETag: "3147526947"
    //   Server: ECS, (cha / 80E2)
    //   X-Cache: HIT
}

Le code précédent :

  • Adresse une demande HEAD à "https://www.example.com/".
  • Vérifie que la réponse est réussie et écrit les détails de la demande dans la console.
  • Itère sur tous les en-têtes de réponse, en écrivant chacun dans la console.

Options HTTP

La requête OPTIONS est utilisée pour identifier les méthodes HTTP prises en charge par un serveur ou un point de terminaison. Pour effectuer une requête OPTIONS HTTP, en fonction d’un HttpClient et d’un URI, utilisez la méthode HttpClient.SendAsync avec la HttpMethod défini sur HttpMethod.Options :

static async Task OptionsAsync(HttpClient httpClient)
{
    using HttpRequestMessage request = new(
        HttpMethod.Options, 
        "https://www.example.com");

    using HttpResponseMessage response = await httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode()
        .WriteRequestToConsole();

    foreach (var header in response.Content.Headers)
    {
        Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
    }
    Console.WriteLine();

    // Expected output
    //   OPTIONS https://www.example.com/ HTTP/1.1
    //   Allow: OPTIONS, GET, HEAD, POST
    //   Content-Type: text/html; charset=utf-8
    //   Expires: Wed, 17 Aug 2022 17:28:42 GMT
    //   Content-Length: 0
}

Le code précédent :

  • Envoie une requête OPTIONS HTTP à "https://www.example.com/".
  • Vérifie que la réponse est réussie et écrit les détails de la demande dans la console.
  • Itère sur tous les en-têtes de contenu de réponse, en écrivant chacun dans la console.

Trace HTTP

La requête TRACE peut être utile pour le débogage, car elle fournit une boucle arrière au niveau de l’application du message de requête. Pour effectuer une requête HTTP TRACE, créez une HttpRequestMessage à l’aide de HttpMethod.Trace :

using HttpRequestMessage request = new(
    HttpMethod.Trace, 
    "{ValidRequestUri}");

Attention

La méthode HTTP TRACE n’est pas prise en charge par tous les serveurs HTTP. Elle peut exposer une vulnérabilité de sécurité si elle est utilisé de manière imprudente. Pour plus d’informations, consultez Open Web Application Security Project (OWASP) : suivi intersite.

Gérer une réponse HTTP

Chaque fois que vous gérez une réponse HTTP, vous interagissez avec le type HttpResponseMessage. Plusieurs membres sont utilisés lors de l’évaluation de la validité d’une réponse. Le code d’état HTTP est disponible via la propriété HttpResponseMessage.StatusCode. Imaginez que vous avez envoyé une requête à une instance cliente :

using HttpResponseMessage response = await httpClient.SendAsync(request);

Pour vous assurer que response est OK (code d’état HTTP 200), vous pouvez l’évaluer comme indiqué dans l’exemple suivant :

if (response is { StatusCode: HttpStatusCode.OK })
{
    // Omitted for brevity...
}

Il existe d’autres codes d’état HTTP qui représentent une réponse réussie, tels que CREATED (code d’état HTTP 201), ACCEPTED (code d’état HTTP 202), NO CONTENT (code d’état HTTP 204) et RESET CONTENT (code d’état HTTP 205). Vous pouvez également utiliser la propriété HttpResponseMessage.IsSuccessStatusCode pour évaluer ces codes, ce qui garantit que le code d’état de la réponse se trouve dans la plage 200-299 :

if (response.IsSuccessStatusCode)
{
    // Omitted for brevity...
}

Si vous avez besoin que l’infrastructure lève le HttpRequestException, vous pouvez appeler la méthode HttpResponseMessage.EnsureSuccessStatusCode() :

response.EnsureSuccessStatusCode();

Ce code lève une HttpRequestException si le code d’état de la réponse ne se trouve pas dans la plage 200-299.

Réponses de contenu valides HTTP

Avec une réponse valide, vous pouvez accéder au corps de la réponse à l’aide de la propriété Content. Le corps est disponible en tant qu’instance HttpContent, que vous pouvez utiliser pour accéder au corps sous la forme d’un flux, d’un tableau d’octets ou d’une chaîne :

await using Stream responseStream =
    await response.Content.ReadAsStreamAsync();

Dans le code précédent, le responseStream peut être utilisé pour lire le corps de la réponse.

byte[] responseByteArray = await response.Content.ReadAsByteArrayAsync();

Dans le code précédent, le responseByteArray peut être utilisé pour lire le corps de la réponse.

string responseString = await response.Content.ReadAsStringAsync();

Dans le code précédent, le responseString peut être utilisé pour lire le corps de la réponse.

Enfin, quand vous savez qu’un point de terminaison HTTP retourne JSON, vous pouvez désérialiser le corps de la réponse dans n’importe quel objet C# valide à l’aide du package NuGet System.Net.Http.Json :

T? result = await response.Content.ReadFromJsonAsync<T>();

Dans le code précédent, result est le corps de la réponse désérialisé en tant que type T.

Gestion des erreurs HTTP

Lorsqu’une requête HTTP échoue, la HttpRequestException est levée. La détection de cette exception seule peut ne pas suffire, car il existe d’autres exceptions potentielles levées que vous souhaiterez peut-être gérer. Par exemple, le code appelant peut avoir utilisé un jeton d’annulation qui a été annulé avant la fin de la demande. Dans ce scénario, vous interceptez :TaskCanceledException

using var cts = new CancellationTokenSource();
try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/sleepFor?seconds=100", cts.Token);
}
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
{
    // When the token has been canceled, it is not a timeout.
    Console.WriteLine($"Canceled: {ex.Message}");
}

De même, lorsque vous effectuez une requête HTTP, si le serveur ne répond pas avant le HttpClient.Timeout est dépassé, la même exception est levée. Toutefois, dans ce scénario, vous pouvez distinguer que le délai d’expiration s’est produit en évaluant le Exception.InnerException lors de la capture de TaskCanceledException :

try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/sleepFor?seconds=100");
}
catch (OperationCanceledException ex) when (ex.InnerException is TimeoutException tex)
{
    Console.WriteLine($"Timed out: {ex.Message}, {tex.Message}");
}

Dans le code précédent, lorsque l’exception interne est une exception, le délai d’expiration TimeoutException s’est produit et la demande n’a pas été annulée par le jeton d’annulation.

Pour évaluer le code d’état HTTP lors de la capture d’un HttpRequestException, vous pouvez évaluer la propriété HttpRequestException.StatusCode :

try
{
    // Assuming:
    //   httpClient.Timeout = TimeSpan.FromSeconds(10)

    using var response = await httpClient.GetAsync(
        "http://localhost:5001/doesNotExist");

    response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    // Handle 404
    Console.WriteLine($"Not found: {ex.Message}");
}

Dans le code précédent, la méthode EnsureSuccessStatusCode() est appelée pour lever une exception si la réponse échoue. La propriété HttpRequestException.StatusCode est ensuite évaluée pour déterminer si la réponse était un 404 (code d’état HTTP 404). Il existe plusieurs méthodes d’assistance sur HttpClient qui appellent EnsureSuccessStatusCode implicitement en votre nom, considérez les API suivantes :

Conseil

Toutes les méthodes HttpClient utilisées pour effectuer des requêtes HTTP qui ne retournent pas HttpResponseMessage appellent implicitement EnsureSuccessStatusCode en votre nom.

Lorsque vous appelez ces méthodes, vous pouvez gérer le HttpRequestException et évaluer la propriété HttpRequestException.StatusCode pour déterminer le code d’état HTTP de la réponse :

try
{
    // These extension methods will throw HttpRequestException
    // with StatusCode set when the HTTP request status code isn't 2xx:
    //
    //   GetByteArrayAsync
    //   GetStreamAsync
    //   GetStringAsync

    using var stream = await httpClient.GetStreamAsync(
        "https://localhost:5001/doesNotExists");
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    // Handle 404
    Console.WriteLine($"Not found: {ex.Message}");
}

Il peut y avoir des scénarios dans lesquels vous devez lever le HttpRequestException dans votre code. Le constructeur HttpRequestException() est public et vous pouvez l’utiliser pour lever une exception avec un message personnalisé :

try
{
    using var response = await httpClient.GetAsync(
        "https://localhost:5001/doesNotExists");

    // Throw for anything higher than 400.
    if (response is { StatusCode: >= HttpStatusCode.BadRequest })
    {
        throw new HttpRequestException(
            "Something went wrong", inner: null, response.StatusCode);
    }
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
    Console.WriteLine($"Not found: {ex.Message}");
}

Serveur proxy HTTP

Un proxy HTTP peut être configuré de l’une des deux façons suivantes. Une valeur par défaut est spécifiée sur la propriété HttpClient.DefaultProxy. Vous pouvez également spécifier un proxy sur la propriété HttpClientHandler.Proxy.

Proxy global par défaut

HttpClient.DefaultProxy est une propriété statique qui détermine le proxy par défaut que toutes les instances HttpClientutilisent si aucun proxy n’est défini explicitement dans le HttpClientHandler passé via son constructeur.

L’instance par défaut retournée par cette propriété s’initialise selon un ensemble différent de règles en fonction de votre plateforme :

  • Pour Windows : lit la configuration du proxy à partir de variables d’environnement ou, si celles-ci ne sont pas définies, à partir des paramètres de proxy de l’utilisateur.
  • Pour macOS : lit la configuration du proxy à partir de variables d’environnement ou, si celles-ci ne sont pas définies, à partir des paramètres de proxy du système.
  • Pour Linux : lit la configuration du proxy à partir de variables d’environnement ou, si celles-ci ne sont pas définies, cette propriété initialise une instance non configurée qui contourne toutes les adresses.

Les variables d’environnement utilisées pour l’initialisation de DefaultProxy sur les plateformes Windows et Unix sont les suivantes :

  • HTTP_PROXY : serveur proxy utilisé sur les demandes HTTP.
  • HTTPS_PROXY : serveur proxy utilisé sur les demandes HTTPS.
  • ALL_PROXY : le serveur proxy utilisé sur les requêtes HTTP et/ou HTTPS dans le cas HTTP_PROXY et/ou HTTPS_PROXY ne sont pas définis.
  • NO_PROXY : liste de noms d’hôte séparés par des virgules à exclure comme proxy. Les astérisques ne sont pas pris en charge pour les caractères génériques ; utilisez un point de début au cas où vous souhaitez faire correspondre un sous-domaine. Exemples : NO_PROXY=.example.com (avec point de début) correspond à www.example.com, mais ne correspond pas à example.com. NO_PROXY=example.com (sans point de début) ne correspond pas à www.example.com. Ce comportement peut être revisité à l’avenir pour mieux correspondre à d’autres écosystèmes.

Sur les systèmes où les variables d’environnement sont sensibles à la casse, les noms des variables peuvent être tout en minuscules ou tout en minuscules. Les noms en minuscules sont vérifiés en premier.

Le serveur proxy peut être un nom d’hôte ou une adresse IP, éventuellement suivi d’un signe deux-points et d’un numéro de port, ou il peut s’agir d’une URL http, y compris éventuellement un nom d’utilisateur et un mot de passe pour l’authentification proxy. L’URL doit commencer par http et non par https et ne peut pas inclure de texte après le nom d’hôte, l’adresse IP ou le port.

Proxy par client

La propriété HttpClientHandler.Proxy identifie l’objet WebProxy à utiliser pour traiter les demandes adressées aux ressources Internet. Pour spécifier qu’aucun proxy ne doit être utilisé, définissez la propriété Proxy sur l’instance de proxy retournée par la méthode GlobalProxySelection.GetEmptyWebProxy().

L’ordinateur local ou le fichier config de l’application peut spécifier qu’un proxy par défaut doit être utilisé. Si la propriété Proxy est spécifiée, les paramètres de proxy de la propriété Proxy remplacent l’ordinateur local ou le fichier config de l’application et le gestionnaire utilise les paramètres de proxy spécifiés. Si aucun proxy n’est spécifié dans un fichier de configuration et que la propriété Proxy n’est pas spécifiée, le gestionnaire utilise les paramètres de proxy hérités de l’ordinateur local. S’il n’existe aucun paramètre de proxy, la demande est envoyée directement au serveur.

La classe HttpClientHandler analyse une liste de contournement de proxy avec des caractères génériques hérités des paramètres de l’ordinateur local. Par exemple, la classe HttpClientHandler analyse une liste de contournement de "nt*" depuis des navigateurs en tant qu’expression régulière de "nt.*". Ainsi, une URL de http://nt.com contourne le proxy à l’aide de la classe HttpClientHandler.

La classe HttpClientHandler prend en charge la déviation du proxy local. La classe considère qu’une destination est locale si l’une des conditions suivantes est remplie :

  1. La destination contient un nom plat (aucun point dans l’URL).
  2. La destination contient une adresse de bouclage (Loopback ou IPv6Loopback) ou la destination contient un IPAddress affecté à l’ordinateur local.
  3. Le suffixe de domaine de la destination correspond au suffixe de domaine de l’ordinateur local (DomainName).

Pour plus d'informations sur la configuration d’un proxy, consultez :

Voir aussi