Appeler une API web à partir d’un client .NET (C#)

Ce contenu est destiné à une version précédente de .NET. Le nouveau développement doit utiliser ASP.NET Core. Pour plus d’informations sur l’utilisation de l’API web ASP.NET Core, consultez :

Téléchargez le projet terminé.

Ce tutoriel montre comment appeler une API web à partir d’une application .NET à l’aide de System.Net.Http.HttpClient.

Dans ce tutoriel, une application cliente qui utilise l’API web suivante est écrite :

Action HTTP method URI relatif
Obtenir un produit par ID GET /api/products/id
Créer un produit POST /api/products
Mettre à jour un produit PUT /api/products/id
Supprimer un produit Suppression /api/products/id

Pour savoir comment implémenter cette API avec API Web ASP.NET, consultez Création d’une API web qui prend en charge les opérations CRUD.

Par souci de simplicité, l’application cliente de ce didacticiel est une application console Windows. HttpClient est également pris en charge pour les applications Windows Phone et du Windows Store. Pour plus d’informations, consultez Écriture de code client d’API web pour plusieurs plateformes à l’aide de bibliothèques portables

NOTE: Si vous passez des URL de base et des URI relatifs en tant que valeurs codées en dur, gardez à l’esprit les règles d’utilisation de l’API HttpClient . La HttpClient.BaseAddress propriété doit être définie sur une adresse avec une barre oblique de fin (/). Par exemple, lorsque vous passez des URI de ressource codés en dur à la HttpClient.GetAsync méthode, n’incluez pas de barre oblique de début. Pour obtenir un Product par ID :

  1. Définissez client.BaseAddress = new Uri("https://localhost:5001/");
  2. Demandez un Product. Par exemple : client.GetAsync<Product>("api/products/4");.

Création d'application console

Dans Visual Studio, créez une application console Windows nommée HttpClientSample et collez le code suivant :

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace HttpClientSample
{
    public class Product
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Category { get; set; }
    }

    class Program
    {
        static HttpClient client = new HttpClient();

        static void ShowProduct(Product product)
        {
            Console.WriteLine($"Name: {product.Name}\tPrice: " +
                $"{product.Price}\tCategory: {product.Category}");
        }

        static async Task<Uri> CreateProductAsync(Product product)
        {
            HttpResponseMessage response = await client.PostAsJsonAsync(
                "api/products", product);
            response.EnsureSuccessStatusCode();

            // return URI of the created resource.
            return response.Headers.Location;
        }

        static async Task<Product> GetProductAsync(string path)
        {
            Product product = null;
            HttpResponseMessage response = await client.GetAsync(path);
            if (response.IsSuccessStatusCode)
            {
                product = await response.Content.ReadAsAsync<Product>();
            }
            return product;
        }

        static async Task<Product> UpdateProductAsync(Product product)
        {
            HttpResponseMessage response = await client.PutAsJsonAsync(
                $"api/products/{product.Id}", product);
            response.EnsureSuccessStatusCode();

            // Deserialize the updated product from the response body.
            product = await response.Content.ReadAsAsync<Product>();
            return product;
        }

        static async Task<HttpStatusCode> DeleteProductAsync(string id)
        {
            HttpResponseMessage response = await client.DeleteAsync(
                $"api/products/{id}");
            return response.StatusCode;
        }

        static void Main()
        {
            RunAsync().GetAwaiter().GetResult();
        }

        static async Task RunAsync()
        {
            // Update port # in the following line.
            client.BaseAddress = new Uri("http://localhost:64195/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            try
            {
                // Create a new product
                Product product = new Product
                {
                    Name = "Gizmo",
                    Price = 100,
                    Category = "Widgets"
                };

                var url = await CreateProductAsync(product);
                Console.WriteLine($"Created at {url}");

                // Get the product
                product = await GetProductAsync(url.PathAndQuery);
                ShowProduct(product);

                // Update the product
                Console.WriteLine("Updating price...");
                product.Price = 80;
                await UpdateProductAsync(product);

                // Get the updated product
                product = await GetProductAsync(url.PathAndQuery);
                ShowProduct(product);

                // Delete the product
                var statusCode = await DeleteProductAsync(product.Id);
                Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            Console.ReadLine();
        }
    }
}

Le code précédent est l’application cliente complète.

RunAsync s’exécute et se bloque jusqu’à ce qu’il se termine. La plupart des méthodes HttpClient sont asynchrones, car elles effectuent des E/S réseau. Toutes les tâches asynchrones sont effectuées à l’intérieur de RunAsync. Normalement, une application ne bloque pas le thread main, mais cette application n’autorise aucune interaction.

static async Task RunAsync()
{
    // Update port # in the following line.
    client.BaseAddress = new Uri("http://localhost:64195/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

    try
    {
        // Create a new product
        Product product = new Product
        {
            Name = "Gizmo",
            Price = 100,
            Category = "Widgets"
        };

        var url = await CreateProductAsync(product);
        Console.WriteLine($"Created at {url}");

        // Get the product
        product = await GetProductAsync(url.PathAndQuery);
        ShowProduct(product);

        // Update the product
        Console.WriteLine("Updating price...");
        product.Price = 80;
        await UpdateProductAsync(product);

        // Get the updated product
        product = await GetProductAsync(url.PathAndQuery);
        ShowProduct(product);

        // Delete the product
        var statusCode = await DeleteProductAsync(product.Id);
        Console.WriteLine($"Deleted (HTTP Status = {(int)statusCode})");

    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }

    Console.ReadLine();
}

Installer les bibliothèques clientes d’API web

Utilisez le Gestionnaire de package NuGet pour installer le package bibliothèques clientes d’API web.

Dans le menu Outils, sélectionnez Gestionnaire de package NuGet>Console du Gestionnaire de package. Dans la console du Gestionnaire de package (PMC), tapez la commande suivante :

Install-Package Microsoft.AspNet.WebApi.Client

La commande précédente ajoute les packages NuGet suivants au projet :

  • Microsoft.AspNet.WebApi.Client
  • Newtonsoft.Json

Newtonsoft.Json (également appelé Json.NET) est un framework JSON hautes performances populaire pour .NET.

Ajouter une classe de modèle

Examiner la classe Product :

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
}

Cette classe correspond au modèle de données utilisé par l’API web. Une application peut utiliser HttpClient pour lire un Product instance à partir d’une réponse HTTP. L’application n’a pas besoin d’écrire de code de désérialisation.

Créer et initialiser HttpClient

Examinez la propriété HttpClient statique :

static HttpClient client = new HttpClient();

HttpClient est destiné à être instancié une seule fois et réutilisé tout au long de la vie d’une application. Les conditions suivantes peuvent entraîner des erreurs SocketException :

  • Création d’un instance HttpClient par demande.
  • Serveur sous une charge importante.

La création d’un instance HttpClient par requête peut épuiser les sockets disponibles.

Le code suivant initialise le instance HttpClient :

static async Task RunAsync()
{
    // Update port # in the following line.
    client.BaseAddress = new Uri("http://localhost:64195/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json"));

Le code précédent :

  • Définit l’URI de base pour les requêtes HTTP. Remplacez le numéro de port par le port utilisé dans l’application serveur. L’application ne fonctionnera pas tant que le port de l’application serveur n’est pas utilisé.
  • Définit l’en-tête Accepter sur « application/json ». La définition de cet en-tête indique au serveur d’envoyer des données au format JSON.

Envoyer une demande GET pour récupérer une ressource

Le code suivant envoie une demande GET pour un produit :

static async Task<Product> GetProductAsync(string path)
{
    Product product = null;
    HttpResponseMessage response = await client.GetAsync(path);
    if (response.IsSuccessStatusCode)
    {
        product = await response.Content.ReadAsAsync<Product>();
    }
    return product;
}

La méthode GetAsync envoie la requête HTTP GET. Une fois la méthode terminée, elle retourne un HttpResponseMessage qui contient la réponse HTTP. Si le code status dans la réponse est un code de réussite, le corps de la réponse contient la représentation JSON d’un produit. Appelez ReadAsAsync pour désérialiser la charge utile JSON sur un Product instance. La méthode ReadAsAsync est asynchrone, car le corps de la réponse peut être arbitrairement volumineux.

HttpClient ne lève pas d’exception lorsque la réponse HTTP contient un code d’erreur. Au lieu de cela, la propriété IsSuccessStatusCode a la valeur false si le status est un code d’erreur. Si vous préférez traiter les codes d’erreur HTTP comme des exceptions, appelez HttpResponseMessage.EnsureSuccessStatusCode sur l’objet de réponse. EnsureSuccessStatusCodelève une exception si le code status se situe en dehors de la plage comprise entre 200 et 299. Notez que HttpClient peut lever des exceptions pour d’autres raisons, par exemple si la demande expire.

Media-Type formateurs à désérialiser

Lorsque ReadAsAsync est appelé sans paramètres, il utilise un ensemble par défaut de formateurs multimédias pour lire le corps de la réponse. Les formateurs par défaut prennent en charge les données encodées au format JSON, XML et Form-URL.

Au lieu d’utiliser les formateurs par défaut, vous pouvez fournir une liste de formateurs à la méthode ReadAsAsync . L’utilisation d’une liste de formateurs est utile si vous disposez d’un formateur de type média personnalisé :

var formatters = new List<MediaTypeFormatter>() {
    new MyCustomFormatter(),
    new JsonMediaTypeFormatter(),
    new XmlMediaTypeFormatter()
};
resp.Content.ReadAsAsync<IEnumerable<Product>>(formatters);

Pour plus d’informations, consultez Formateurs multimédias dans API Web ASP.NET 2

Envoi d’une demande POST pour créer une ressource

Le code suivant envoie une requête POST qui contient une Product instance au format JSON :

static async Task<Uri> CreateProductAsync(Product product)
{
    HttpResponseMessage response = await client.PostAsJsonAsync(
        "api/products", product);
    response.EnsureSuccessStatusCode();

    // return URI of the created resource.
    return response.Headers.Location;
}

La méthode PostAsJsonAsync :

  • Sérialise un objet au format JSON.
  • Envoie la charge utile JSON dans une requête POST.

Si la demande réussit :

  • Il doit retourner une réponse 201 (Créé).
  • La réponse doit inclure l’URL des ressources créées dans l’en-tête Location.

Envoi d’une demande PUT pour mettre à jour une ressource

Le code suivant envoie une demande PUT pour mettre à jour un produit :

static async Task<Product> UpdateProductAsync(Product product)
{
    HttpResponseMessage response = await client.PutAsJsonAsync(
        $"api/products/{product.Id}", product);
    response.EnsureSuccessStatusCode();

    // Deserialize the updated product from the response body.
    product = await response.Content.ReadAsAsync<Product>();
    return product;
}

La méthode PutAsJsonAsync fonctionne comme PostAsJsonAsync, sauf qu’elle envoie une requête PUT au lieu de POST.

Envoi d’une demande DELETE pour supprimer une ressource

Le code suivant envoie une demande DELETE pour supprimer un produit :

static async Task<HttpStatusCode> DeleteProductAsync(string id)
{
    HttpResponseMessage response = await client.DeleteAsync(
        $"api/products/{id}");
    return response.StatusCode;
}

Comme GET, une demande DELETE n’a pas de corps de requête. Vous n’avez pas besoin de spécifier le format JSON ou XML avec DELETE.

Tester l’exemple

Pour tester l’application cliente :

  1. Téléchargez et exécutez l’application serveur. Vérifiez que l’application serveur fonctionne. Par exemple, http://localhost:64195/api/products doit retourner une liste de produits.

  2. Définissez l’URI de base pour les requêtes HTTP. Remplacez le numéro de port par le port utilisé dans l’application serveur.

    static async Task RunAsync()
    {
        // Update port # in the following line.
        client.BaseAddress = new Uri("http://localhost:64195/");
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));
    
  3. Exécutez l’application cliente. La sortie suivante est produite :

    Created at http://localhost:64195/api/products/4
    Name: Gizmo     Price: 100.0    Category: Widgets
    Updating price...
    Name: Gizmo     Price: 80.0     Category: Widgets
    Deleted (HTTP Status = 204)