Поделиться через


Вызов веб-API из клиента .NET (C#)

Это содержимое предназначено для предыдущей версии .NET. Новые разработки должны использовать ASP.NET Core. Дополнительные сведения об использовании веб-API ASP.NET Core см. в разделе:

Скачайте завершенный проект.

В этом руководстве показано, как вызвать веб-API из приложения .NET с помощью System.Net.Http.HttpClient.

В этом руководстве создается клиентское приложение, которое использует следующий веб-API:

Действие Метод HTTP Относительный URI
Получение сведений о продукте по идентификатору GET /api/products/id
Создание продукта POST /api/products
Обновить продукт PUT /api/products/id
Удалить продукт DELETE /api/products/id

Сведения о том, как реализовать этот API с помощью веб-API ASP.NET, см. в статье Создание веб-API, поддерживающего операции CRUD.

Для простоты клиентское приложение в этом руководстве является консольным приложением Windows. HttpClient также поддерживается для приложений Windows Phone и Магазина Windows. Дополнительные сведения см. в статье Написание клиентского кода веб-API для нескольких платформ с помощью переносимых библиотек.

ПРИМЕЧАНИЕ: Если вы передаете базовые URL-адреса и относительные URI в виде жестко заданных значений, помните о правилах использования HttpClient API. Для HttpClient.BaseAddress свойства должен быть задан адрес с косой чертой в конце (/). Например, при передаче жестко заданных URI ресурсов в HttpClient.GetAsync метод не включайте косую черту в начале. Чтобы получить по идентификатору, выполните приведенные Product далее действия.

  1. Задайте значение client.BaseAddress = new Uri("https://localhost:5001/");.
  2. ProductЗапросите . Например, client.GetAsync<Product>("api/products/4");.

Создание консольного приложения

В Visual Studio создайте консольное приложение Windows с именем HttpClientSample и вставьте следующий код:

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

Приведенный выше код является полным клиентским приложением.

RunAsync выполняется и блокируется, пока не завершится. Большинство методов HttpClient являются асинхронными, так как они выполняют сетевые операции ввода-вывода. Все асинхронные задачи выполняются внутри RunAsync. Обычно приложение не блокирует поток main, но оно не разрешает взаимодействие.

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

Установка клиентских библиотек веб-API

Используйте диспетчер пакетов NuGet для установки пакета клиентских библиотек веб-API.

В меню Инструменты выберите Диспетчер пакетов NuGet>Консоль диспетчера пакетов. В консоли диспетчера пакетов (PMC) введите следующую команду:

Install-Package Microsoft.AspNet.WebApi.Client

Предыдущая команда добавляет в проект следующие пакеты NuGet:

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

Newtonsoft.Json (также известный как Json.NET) — это популярная высокопроизводительная платформа JSON для .NET.

Добавление класса модели

Проверьте класс Product:

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

Этот класс соответствует модели данных, используемой веб-API. Приложение может использовать HttpClient для чтения экземпляра Product из HTTP-ответа. Приложению не нужно писать код десериализации.

Создание и инициализация HttpClient

Изучите статическое свойство HttpClient :

static HttpClient client = new HttpClient();

HttpClient предназначен для создания экземпляра один раз и повторного использования на протяжении всего времени существования приложения. Следующие условия могут привести к ошибкам SocketException :

  • Создание нового экземпляра HttpClient для каждого запроса.
  • Сервер под большой нагрузкой.

Создание нового экземпляра HttpClient для каждого запроса может исчерпать доступные сокеты.

Следующий код инициализирует экземпляр 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"));

Предыдущий код:

  • Задает базовый URI для HTTP-запросов. Измените номер порта на порт, используемый в серверном приложении. Приложение не будет работать, если не используется порт для серверного приложения.
  • Задает для заголовка Accept значение application/json. Если задать этот заголовок, сервер будет отправлять данные в формате JSON.

Отправка запроса GET для получения ресурса

Следующий код отправляет запрос GET для продукта:

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

Метод GetAsync отправляет HTTP-запрос GET. После завершения метода возвращается httpResponseMessage , содержащий HTTP-ответ. Если код состояния в ответе является кодом успешного выполнения, текст ответа содержит представление продукта в формате JSON. Вызовите ReadAsAsync для десериализации полезных данных JSON в Product экземпляр. Метод ReadAsAsync является асинхронным, так как текст ответа может быть произвольно большим.

HttpClient не создает исключение, если HTTP-ответ содержит код ошибки. Вместо этого свойство IsSuccessStatusCode имеет значение false , если состояние является кодом ошибки. Если вы предпочитаете обрабатывать коды ошибок HTTP как исключения, вызовите HttpResponseMessage.EnsureSuccessStatusCode в объекте ответа. EnsureSuccessStatusCode Создает исключение, если код состояния выходит за пределы диапазона 200–299. Обратите внимание, что HttpClient может создавать исключения по другим причинам, например, если истекает время ожидания запроса.

Media-Type форматировщиков для десериализации

При вызове ReadAsAsync без параметров он использует набор форматировщиков мультимедиа по умолчанию для чтения текста ответа. Модули форматирования по умолчанию поддерживают данные в кодировке JSON, XML и Form-URL.

Вместо использования модулей форматирования по умолчанию можно предоставить список модулей форматирования для метода ReadAsAsync . Использование списка модулей форматирования полезно при наличии настраиваемого модуля форматирования типа мультимедиа:

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

Дополнительные сведения см. в разделе Форматировщики мультимедиа в веб-API ASP.NET 2.

Отправка запроса POST для создания ресурса

Следующий код отправляет запрос POST, содержащий Product экземпляр в формате 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;
}

Метод PostAsJsonAsync :

  • Сериализует объект в формате JSON.
  • Отправляет полезные данные JSON в запросе POST.

Если запрос выполнен успешно:

  • Он должен вернуть ответ 201 (создано).
  • Ответ должен содержать URL-адрес созданных ресурсов в заголовке Location.

Отправка запроса PUT на обновление ресурса

Следующий код отправляет запрос PUT на обновление продукта:

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

Метод PutAsJsonAsync работает так же, как PostAsJsonAsync, за исключением того, что он отправляет запрос PUT вместо POST.

Отправка запроса DELETE на удаление ресурса

Следующий код отправляет запрос DELETE на удаление продукта:

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

Как и GET, запрос DELETE не имеет текста запроса. Вам не нужно указывать формат JSON или XML с помощью DELETE.

Тестирование примера

Чтобы протестировать клиентское приложение, выполните следующие действия.

  1. Скачайте и запустите серверного приложения. Убедитесь, что серверный приложение работает. Например, http://localhost:64195/api/products должен возвращать список продуктов.

  2. Задайте базовый URI для HTTP-запросов. Измените номер порта на порт, используемый в серверном приложении.

    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. Запустите клиентское приложение. Выводятся следующие результаты.

    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)