Tutoriel : Effectuer des requêtes HTTP dans une application console .NET en utilisant C#

Ce didacticiel génère une application qui émet des requêtes HTTP vers un service REST sur GitHub. L’application lit les informations au format JSON et les convertit en objets C#. La conversion d’objets JSON en objets C# est appelée désérialisation.

Le didacticiel montre comment :

  • Envoyer des requêtes HTTP.
  • Désérialiser les réponses JSON.
  • Configurer la désérialisation avec des attributs.

Si vous préférez utiliser l’échantillon final pour ce didacticiel, vous pouvez le télécharger. Pour obtenir des instructions de téléchargement, consultez Exemples et didacticiels.

Prérequis

  • SDK .NET 6.0 ou ultérieur
  • Un éditeur de code tel que [Visual Studio Code (éditeur multiplateforme open source). Vous pouvez exécuter l’échantillon d’application sous Windows, Linux ou macOS, ou dans un conteneur Docker.

Créer l'application cliente

  1. Ouvrez une invite de commandes et créez un nouveau répertoire pour votre application. Réglez-le comme répertoire actuel.

  2. Entrez la commande suivante dans la fenêtre de console :

    dotnet new console --name WebAPIClient
    

    Cette commande crée les fichiers de démarrage d’une application « Hello World » de base. Le nom du projet est « WebAPIClient ».

  3. Accédez au répertoire « WebAPIClient » et exécutez l’application.

    cd WebAPIClient
    
    dotnet run
    

    dotnet run s’exécute dotnet restore automatiquement pour restaurer toutes les dépendances dont l’application a besoin. Exécute dotnet build également si nécessaire. Vous devez voir la sortie "Hello, World!" de l’application. Dans votre terminal, appuyez sur Ctrl+C pour arrêter l’application.

Effectuer des requêtes HTTP

Cette application appelle l’API GitHub pour obtenir des informations sur les projets sous .NET Foundation . Le point de terminaison a la valeur https://api.github.com/orgs/dotnet/repos. Pour récupérer des informations, elle effectue une requête HTTP GET. Les navigateurs font également des requêtes HTTP GET, vous pouvez donc coller cette URL dans votre barre d'adresse pour voir les informations que vous recevez et traitez.

Utilisez la HttpClient classe pour effectuer des requêtes HTTP. HttpClient prend en charge uniquement les méthodes Async pour ses API de longue durée. Ainsi, les étapes suivantes créent une méthode asynchrone et l’appellent à partir de la méthode Main.

  1. Ouvrez le fichier Program.cs dans votre répertoire de projet et remplacez son contenu par ceci :

    await ProcessRepositoriesAsync();
    
    static async Task ProcessRepositoriesAsync(HttpClient client)
    {
    }
    

    Ce code :

    • Remplace l’instruction Console.WriteLine par un appel à ProcessRepositoriesAsync qui utilise le mot clé await.
    • Définit une méthode ProcessRepositoriesAsync vide.
  2. Dans la classe Program, utilisez un HttpClient pour gérer les demandes et les réponses, en remplaçant le contenu par le C# suivant.

    using System.Net.Http.Headers;
    
    using HttpClient client = new();
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
    client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter");
    
    await ProcessRepositoriesAsync(client);
    
    static async Task ProcessRepositoriesAsync(HttpClient client)
    {
    }
    

    Ce code :

    • Configure les en-têtes HTTP pour toutes les requêtes :
      • Un en-tête Accept pour accepter les réponses JSON
      • En-tête de User-Agent. Ces deux en-têtes sont vérifiés par le code du serveur GitHub et sont nécessaires pour récupérer des informations à partir de GitHub.
  3. Dans la méthodeProcessRepositoriesAsync, appelez le point de terminaison GitHub qui retourne une liste de tous les référentiels sous l’organisation .NET Foundation :

     static async Task ProcessRepositoriesAsync(HttpClient client)
     {
         var json = await client.GetStringAsync(
             "https://api.github.com/orgs/dotnet/repos");
    
         Console.Write(json);
     }
    

    Ce code :

    • Attend la tâche retournée par la méthode appelante HttpClient.GetStringAsync(String). Cette méthode envoie une requête HTTP GET à l’URI spécifié. Le corps de la réponse est retourné en tant que String, qui est disponible lorsque la tâche se termine.
    • La chaîne de réponse json est imprimée dans la console.
  4. Générez l'application et exécutez-la.

    dotnet run
    

    Il n’y a pas d’avertissement de build, car ProcessRepositoriesAsync contient maintenant un opérateur await. La sortie est un long affichage du texte JSON.

Désérialiser le résultat JSON

Les étapes suivantes convertissent la réponse JSON en objets C#. Vous utilisez la classe System.Text.Json.JsonSerializer pour désérialiser JSON en objets.

  1. Créez un fichier nommé Repository.cs et ajoutez le code suivant :

    public record class Repository(string name);
    

    Le code précédent définit une classe pour représenter l’objet JSON retourné par l’API GitHub. Vous utiliserez cette classe pour afficher une liste de noms de référentiels.

    Le JSON d’un objet de référentiel contient des dizaines de propriétés, mais seule la propriété name sera désérialisée. Le sérialiseur ignore automatiquement les propriétés JSON pour lesquelles il n’existe aucune correspondance dans la classe cible. Cette fonctionnalité facilite la création de types qui fonctionnent avec uniquement un sous-ensemble des champs dans un paquet JSON volumineux.

    La convention C# consiste à mettre en majuscules la première lettre des noms de propriétés, mais la propriété name commence ici par une lettre minuscule, car cela correspond exactement à ce qui se trouve dans le JSON. Plus tard, vous verrez comment utiliser des noms de propriétés C# qui ne correspondent pas aux noms de propriétés JSON.

  2. Utilisez le sérialiseur pour convertir le JSON en objets C#. Remplacez l’appel à GetStringAsync(String) dans la méthode ProcessRepositoriesAsync avec les lignes suivantes :

    await using Stream stream =
        await client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
    var repositories =
        await JsonSerializer.DeserializeAsync<List<Repository>>(stream);
    

    Le code mis à jour remplace GetStringAsync(String) par GetStreamAsync(String). Cette méthode de sérialiseur utilise un flux plutôt qu’une chaîne comme source.

    Le premier argument JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken) est une expression await. Les expressions await peuvent apparaître quasiment n’importe où dans votre code, bien que jusqu'à présent, vous les avez uniquement vues dans le cadre d’une instruction d’assignation. Les deux autres paramètres, JsonSerializerOptions et CancellationToken, sont facultatifs et sont omis dans l’extrait de code.

    La DeserializeAsync méthode est générique, ce qui signifie que vous fournissez des arguments de type pour le type d’objets à créer à partir du texte JSON. Dans cet exemple, vous désérialisez un List<Repository>, qui est un autre objet générique, un System.Collections.Generic.List<T>. La classe List<T> stocke une collection d’objets. L’argument type déclare le type d’objets stockés dans la List<T>. L’argument type est votre enregistrement Repository, car le texte JSON représente une collection d’objets de référentiel.

  3. Ajoutez du code pour afficher le nom de chaque référentiel. Remplacez les lignes :

    Console.Write(json);
    

    par le code suivant :

    foreach (var repo in repositories ?? Enumerable.Empty<Repository>())
        Console.Write(repo.name);
    
  4. Les directives suivantes using doivent être présentes en haut du fichier :

    using System.Net.Http.Headers;
    using System.Text.Json;
    
  5. Exécutez l’application.

    dotnet run
    

    La sortie est une liste des noms des référentiels qui font partie de la .NET Foundation.

Configurer la désérialisation

  1. Dans Repository.cs, remplacez le contenu du fichier par le code C# suivant.

    using System.Text.Json.Serialization;
    
    public record class Repository(
        [property: JsonPropertyName("name")] string Name);
    

    Ce code :

    • Remplace le nom de la propriété name par Name.
    • Ajoute le JsonPropertyNameAttribute pour spécifier comment cette propriété apparaît dans le JSON.
  2. Dans Program.cs, mettez à jour le code pour utiliser la nouvelle majuscule de la propriété Name :

    foreach (var repo in repositories)
       Console.Write(repo.Name);
    
  3. Exécutez l’application.

    La sortie est la même.

Refactoriser le code

La méthode ProcessRepositoriesAsync peut faire le travail de façon asynchrone et renvoyer une collection des espaces de stockage. Modifiez cette méthode pour retourner Task<List<Repository>>, puis déplacez le code qui écrit dans la console près de son appelant.

  1. Modifiez la signature de ProcessRepositoriesAsync pour retourner une tâche dont le résultat est une liste d’objets Repository :

    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    
  2. Retournez simplement les dépôts après le traitement de la réponse JSON :

    await using Stream stream =
        await client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
    var repositories =
        await JsonSerializer.DeserializeAsync<List<Repository>>(stream);
    return repositories ?? new();
    

    Le compilateur génère l’objet Task<T> pour la valeur renvoyée, car vous avez marqué cette méthode comme async.

  3. Modifiez le fichier Program.cs, en remplaçant l’appel à ProcessRepositoriesAsync par ce qui suit pour capturer les résultats et écrire le nom de chaque référentiel dans la console.

    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
        Console.Write(repo.Name);
    
  4. Exécutez l’application.

    La sortie est la même.

Désérialiser d’autres propriétés

Les étapes suivantes ajoutent du code pour traiter davantage de propriétés dans le paquet JSON reçu. Il est peu probable que vous souhaitiez traiter toutes les propriétés, mais en ajouter quelques autres permet de démontrer d’autres fonctionnalités de C#.

  1. Remplacez le contenu de la classe Repository par la définition suivante record :

    using System.Text.Json.Serialization;
    
    public record class Repository(
        [property: JsonPropertyName("name")] string Name,
        [property: JsonPropertyName("description")] string Description,
        [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl,
        [property: JsonPropertyName("homepage")] Uri Homepage,
        [property: JsonPropertyName("watchers")] int Watchers);
    

    Les Uri types et int disposent de fonctionnalités intégrées pour les convertir vers et à partir de la représentation sous forme de chaîne. Aucun code supplémentaire n’est nécessaire pour désérialiser ces types cibles à partir du format de chaîne JSON. Si le paquet JSON contient des données qui ne sont pas converties en type cible, l’action de sérialisation lève une exception.

  2. Mettez à jour la foreach boucle dans le fichier Program.cs pour afficher les valeurs de propriété :

    foreach (var repo in repositories)
    {
        Console.WriteLine($"Name: {repo.Name}");
        Console.WriteLine($"Homepage: {repo.Homepage}");
        Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}");
        Console.WriteLine($"Description: {repo.Description}");
        Console.WriteLine($"Watchers: {repo.Watchers:#,0}");
        Console.WriteLine();
    }
    
  3. Exécutez l’application.

    La liste inclut désormais les propriétés supplémentaires.

Ajouter une propriété de date

La date de la dernière opération push est mise en forme de cette façon dans la réponse JSON :

2016-02-08T21:27:00Z

Ce format est pour le temps universel coordonné (UTC), de sorte que le résultat de la désérialisation est une DateTime valeur dont Kind la propriété est Utc.

Pour obtenir une date et une heure représentées dans votre fuseau horaire, vous devez écrire une méthode de conversion personnalisée.

  1. Dans Repository.cs, ajoutez une propriété pour la représentation UTC de la date et de l’heure et une propriété readonly LastPush qui retourne la date convertie en heure locale, le fichier doit ressembler à ce qui suit :

    using System.Text.Json.Serialization;
    
    public record class Repository(
        [property: JsonPropertyName("name")] string Name,
        [property: JsonPropertyName("description")] string Description,
        [property: JsonPropertyName("html_url")] Uri GitHubHomeUrl,
        [property: JsonPropertyName("homepage")] Uri Homepage,
        [property: JsonPropertyName("watchers")] int Watchers,
        [property: JsonPropertyName("pushed_at")] DateTime LastPushUtc)
    {
        public DateTime LastPush => LastPushUtc.ToLocalTime();
    }
    

    La propriété LastPush est définie à l’aide d’un membre expression-bodied pour l’accesseur get . Il n’y a pas d’accesseur set. Omettre l’accesseur set est une façon de définir une propriété en lecture seule en C#. (Oui, vous pouvez créer des propriétés en écriture seule en C#, mais leur intérêt est limité.)

  2. Ajoutez une autre instruction de sortie dans Program.cs à nouveau :

    Console.WriteLine($"Last push: {repo.LastPush}");
    
  3. L’application complète doit ressembler au fichier Program.cs suivant :

    using System.Net.Http.Headers;
    using System.Text.Json;
    
    using HttpClient client = new();
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/vnd.github.v3+json"));
    client.DefaultRequestHeaders.Add("User-Agent", ".NET Foundation Repository Reporter");
    
    var repositories = await ProcessRepositoriesAsync(client);
    
    foreach (var repo in repositories)
    {
        Console.WriteLine($"Name: {repo.Name}");
        Console.WriteLine($"Homepage: {repo.Homepage}");
        Console.WriteLine($"GitHub: {repo.GitHubHomeUrl}");
        Console.WriteLine($"Description: {repo.Description}");
        Console.WriteLine($"Watchers: {repo.Watchers:#,0}");
        Console.WriteLine($"{repo.LastPush}");
        Console.WriteLine();
    }
    
    static async Task<List<Repository>> ProcessRepositoriesAsync(HttpClient client)
    {
        await using Stream stream =
            await client.GetStreamAsync("https://api.github.com/orgs/dotnet/repos");
        var repositories =
            await JsonSerializer.DeserializeAsync<List<Repository>>(stream);
        return repositories ?? new();
    }
    
  4. Exécutez l’application.

    La sortie inclut la date et l’heure du dernier envoi (push) à chaque référentiel.

Étapes suivantes

Dans ce didacticiel, vous avez créé une application qui effectue des requêtes web et analyse les résultats. Votre version de l’application doit maintenant correspondre à l’échantillon terminé.

Pour en savoir plus sur la configuration de la sérialisation JSON, consultez Guide pratique pour sérialiser et désérialiser (marshaler et démarshaler) JSON dans .NET.