Wywoływanie internetowego interfejsu API z klienta platformy .NET (C#)

Ta zawartość dotyczy poprzedniej wersji platformy .NET. Nowe programowanie powinno używać ASP.NET Core. Aby uzyskać więcej informacji na temat korzystania z internetowego interfejsu API ASP.NET Core, zobacz:

Pobierz ukończony projekt.

W tym samouczku pokazano, jak wywołać internetowy interfejs API z aplikacji .NET przy użyciu elementu System.Net.Http.HttpClient.

W tym samouczku napisana jest aplikacja kliencka, która korzysta z następującego internetowego interfejsu API:

Akcja Metoda HTTP Względny identyfikator URI
Pobieranie produktu według identyfikatora GET /api/products/id
Tworzenie nowego produktu POST /api/products
Aktualizowanie produktu PUT /api/products/id
Usuwanie produktu DELETE /api/products/id

Aby dowiedzieć się, jak zaimplementować ten interfejs API za pomocą ASP.NET internetowego interfejsu API, zobacz Tworzenie internetowego interfejsu API obsługującego operacje CRUD.

Dla uproszczenia aplikacja kliencka w tym samouczku jest aplikacją konsolową systemu Windows. Klient HttpClient jest również obsługiwany w przypadku aplikacji Windows Phone i Sklepu Windows. Aby uzyskać więcej informacji, zobacz Pisanie kodu klienta internetowego interfejsu API dla wielu platform przy użyciu bibliotek przenośnych

UWAGA: Jeśli przekazujesz podstawowe adresy URL i względne adresy URL jako wartości zakodowane na podstawie kodu, należy pamiętać o regułach używania interfejsu HttpClient API. Właściwość HttpClient.BaseAddress powinna być ustawiona na adres z ukośnikiem/ (). Na przykład w przypadku przekazywania zakodowanych identyfikatorów URI zasobów do HttpClient.GetAsync metody nie dołączaj wiodącego ukośnika. Aby uzyskać identyfikator Product według:

  1. Ustawić client.BaseAddress = new Uri("https://localhost:5001/");
  2. Zażądaj .Product Na przykład client.GetAsync<Product>("api/products/4");.

Tworzenie aplikacji konsolowej

W programie Visual Studio utwórz nową aplikację konsolową systemu Windows o nazwie HttpClientSample i wklej następujący kod:

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

Powyższy kod to kompletna aplikacja kliencka.

RunAsync uruchamia i blokuje, dopóki nie zostanie ukończony. Większość metod HttpClient jest asynchronicznych, ponieważ wykonują operacje we/wy sieci. Wszystkie zadania asynchroniczne są wykonywane wewnątrz RunAsyncelementu . Zwykle aplikacja nie blokuje głównego wątku, ale ta aplikacja nie zezwala na żadną interakcję.

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

Instalowanie bibliotek klienckich internetowego interfejsu API

Użyj Menedżera pakietów NuGet, aby zainstalować pakiet bibliotek klienckich interfejsu API sieci Web.

Z menu Narzędzia wybierz pozycję Konsola menedżera pakietów NuGetPackage Manager>. W konsoli menedżera pakietów (PMC) wpisz następujące polecenie:

Install-Package Microsoft.AspNet.WebApi.Client

Poprzednie polecenie dodaje następujące pakiety NuGet do projektu:

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

Newtonsoft.Json (znany również jako Json.NET) to popularna platforma JSON o wysokiej wydajności dla platformy .NET.

Dodawanie klasy modelu

Sprawdź klasę Product :

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

Ta klasa jest zgodna z modelem danych używanym przez internetowy interfejs API. Aplikacja może użyć klienta HttpClient do odczytu Product wystąpienia z odpowiedzi HTTP. Aplikacja nie musi pisać żadnego kodu deserializacji.

Tworzenie i inicjowanie klienta HttpClient

Sprawdź statyczną właściwość HttpClient :

static HttpClient client = new HttpClient();

Obiekt HttpClient jest przeznaczony do utworzenia wystąpienia raz i ponownego użycia przez cały okres życia aplikacji. Następujące warunki mogą spowodować błędy SocketException :

  • Tworzenie nowego wystąpienia klienta HttpClient na żądanie.
  • Serwer pod dużym obciążeniem.

Utworzenie nowego wystąpienia klienta HttpClient na żądanie może wyczerpać dostępne gniazda.

Poniższy kod inicjuje wystąpienie 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"));

Powyższy kod ma następujące działanie:

  • Ustawia podstawowy identyfikator URI dla żądań HTTP. Zmień numer portu na port używany w aplikacji serwera. Aplikacja nie będzie działać, chyba że jest używany port dla aplikacji serwera.
  • Ustawia nagłówek Accept na wartość "application/json". Ustawienie tego nagłówka informuje serwer o wysyłaniu danych w formacie JSON.

Wysyłanie żądania GET w celu pobrania zasobu

Poniższy kod wysyła żądanie GET dla produktu:

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

Metoda GetAsync wysyła żądanie HTTP GET. Po zakończeniu działania metody zwracany jest komunikat HttpResponseMessage zawierający odpowiedź HTTP. Jeśli kod stanu w odpowiedzi jest kodem sukcesu, treść odpowiedzi zawiera reprezentację JSON produktu. Wywołaj metodę ProductReadAsAsync w celu deserializacji ładunku JSON do wystąpienia. Metoda ReadAsAsync jest asynchroniczna, ponieważ treść odpowiedzi może być dowolnie duża.

Obiekt HttpClient nie zgłasza wyjątku, gdy odpowiedź HTTP zawiera kod błędu. Zamiast tego właściwość IsSuccessStatusCode ma wartość false , jeśli stan jest kodem błędu. Jeśli wolisz traktować kody błędów HTTP jako wyjątki, wywołaj metodę HttpResponseMessage.EnsureSuccessStatusCode w obiekcie odpowiedzi. EnsureSuccessStatusCode zgłasza wyjątek, jeśli kod stanu wykracza poza zakres 200–299. Należy pamiętać, że klient HttpClient może zgłaszać wyjątki z innych powodów — na przykład jeśli upłynął limit czasu żądania.

Media-Type formatery do deserializacji

Gdy funkcja ReadAsAsync jest wywoływana bez parametrów, używa domyślnego zestawu formaterów multimediów do odczytywania treści odpowiedzi. Domyślne formatery obsługują dane zakodowane w formatach JSON, XML i Form-url.

Zamiast używać domyślnych metod formatowania, można podać listę formaterów do metody ReadAsAsync . Korzystanie z listy formaterów jest przydatne, jeśli masz niestandardowy formater typu nośnika:

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

Aby uzyskać więcej informacji, zobacz Media Formatters in ASP.NET Web API 2 (Elementy formatujące nośniki w interfejsie API sieci Web 2)

Wysyłanie żądania POST w celu utworzenia zasobu

Poniższy kod wysyła żądanie POST zawierające Product wystąpienie w formacie 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;
}

Metoda PostAsJsonAsync :

  • Serializuje obiekt w formacie JSON.
  • Wysyła ładunek JSON w żądaniu POST.

Jeśli żądanie powiedzie się:

  • Powinna zostać zwrócona odpowiedź 201 (utworzona).
  • Odpowiedź powinna zawierać adres URL utworzonych zasobów w nagłówku Lokalizacja.

Wysyłanie żądania PUT w celu zaktualizowania zasobu

Poniższy kod wysyła żądanie PUT w celu zaktualizowania produktu:

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

Metoda PutAsJsonAsync działa jak PostAsJsonAsync, z wyjątkiem tego, że wysyła żądanie PUT zamiast POST.

Wysyłanie żądania DELETE w celu usunięcia zasobu

Poniższy kod wysyła żądanie DELETE w celu usunięcia produktu:

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

Podobnie jak GET, żądanie DELETE nie ma treści żądania. Nie musisz określać formatu JSON ani XML przy użyciu polecenia DELETE.

Testowanie przykładu

Aby przetestować aplikację kliencka:

  1. Pobierz i uruchom aplikację serwera. Sprawdź, czy aplikacja serwera działa. Na przykład http://localhost:64195/api/products należy zwrócić listę produktów.

  2. Ustaw podstawowy identyfikator URI dla żądań HTTP. Zmień numer portu na port używany w aplikacji serwera.

    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. Uruchom aplikację kliencką. Zostaną wyświetlone następujące dane wyjściowe:

    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)