Wywoływanie internetowego interfejsu API z poziomu 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 platformy 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 interfejsu API:

Action metoda HTTP Względny identyfikator URI
Pobierz produkt według identyfikatora GET /api/products/id
Tworzenie nowego produktu POST /api/products
Aktualizowanie produktu POŁÓŻ /api/products/id
Usuwanie produktu USUŃ /api/products/id

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

Dla uproszczenia aplikacja kliencka w tym samouczku jest aplikacją konsolową systemu Windows. HttpClient jest również obsługiwany w aplikacjach dla Windows Phone i Windows Store. 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, pamiętaj o regułach używania interfejsu HttpClient API. Właściwość HttpClient.BaseAddress powinna być ustawiona na adres z ukośnikiem końcowym (/). Na przykład podczas przekazywania trwale zakodowanych identyfikatorów URI zasobów do HttpClient.GetAsync metody nie dołączaj wiodącego ukośnika do przodu. Aby uzyskać Product według ID:

  1. Zbiór client.BaseAddress = new Uri("https://localhost:5001/");
  2. Zażądaj żądania 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.

W menu Tools wybierz NuGet Package Manager>Package Manager Console. 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 odczytania 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 raz i ponownego wykorzystania w całej aplikacji. Następujące warunki mogą spowodować błędy SocketException :

  • Tworzenie nowego wystąpienia HttpClient dla każdego żądania.
  • Serwer pod dużym obciążeniem.

Utworzenie nowego wystąpienia HttpClient dla każdego żądania 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"));

Poprzedni kod:

  • 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 metody zwraca ona komunikat HttpResponseMessage zawierający odpowiedź HTTP. Jeśli kod stanu w odpowiedzi to kod powodzenia, treść odpowiedzi zawiera reprezentację JSON produktu. Wywołaj metodę ReadAsAsync, aby zdeserializować ładowanie JSON do wystąpienia Product. 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 to kod 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 znajduje się poza zakresem 200–299. Należy pamiętać, że klient HttpClient może zgłaszać wyjątki z innych powodów — na przykład jeśli limit czasu żądania zostanie upłynął.

Formatery typów mediów 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 formacie JSON, XML i Form-url.

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

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 (Formatery multimediów w interfejsie API sieci Web 2)

Wysyłanie żądania POST w celu utworzenia zasobu

Poniższy kod wysyła żądanie POST zawierające instancję Product 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 zwrócić odpowiedź 201 (Utworzono).
  • 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 tą różnicą, ż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 za pomocą polecenia DELETE.

Testowanie przykładu

Aby przetestować aplikację kliencką:

  1. Pobierz i uruchom aplikację serwera. Sprawdź, czy aplikacja serwera działa. Na przykład http://localhost:64195/api/products powinna 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ą. Zostanie wyświetlony następujący wynik:

    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)