HTTP клиент на C#: делаем запросы правильно

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: зачем нужен правильный HTTP клиент в C#

В современной разработке на платформе .NET практически ни одно приложение не обходится без взаимодействия с удалёнными серверами. Будь то получение данных с REST API, отправка форм на веб-сервер или загрузка файлов — всё это требует грамотной реализации HTTP-запросов. В C# существует несколько подходов, но наиболее современным и рекомендуемым является класс HttpClient из пространства имён System.Net.Http. В этой статье мы разберём, как правильно создавать HTTP клиент на C#, избегать типичных ошибок и писать эффективный код.

Основные классы для работы с HTTP в C# (.NET)

Прежде чем перейти к примерам, важно понять, какие инструменты предоставляет платформа .NET. Основные классы:

  • HttpClient — основной класс для отправки запросов и получения ответов. Он должен использоваться как синглтон (один экземпляр на всё приложение).
  • HttpRequestMessage — позволяет детально настроить запрос: метод, заголовки, тело, версию протокола.
  • HttpResponseMessage — содержит ответ от сервера: статус, заголовки, тело.
  • HttpContent — базовый класс для представления тела запроса или ответа (например, StringContent, ByteArrayContent, MultipartFormDataContent).

Ключевое правило: не создавайте новый экземпляр HttpClient для каждого запроса. Это приводит к исчерпанию сокетов и снижению производительности. Используйте один статический или внедрённый экземпляр.

Настройка HttpClient: базовый пример

Рассмотрим минимальную настройку. Создадим экземпляр HttpClient и зададим базовый адрес:

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

public class ApiService
{
    private static readonly HttpClient client = new HttpClient();

    public ApiService()
    {
        client.BaseAddress = new Uri("https://api.example.com/");
        client.DefaultRequestHeaders.Add("User-Agent", "C# HttpClient App");
        client.DefaultRequestHeaders.Accept.Add(
            new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
    }

    public async Task<string> GetDataAsync(string endpoint)
    {
        HttpResponseMessage response = await client.GetAsync(endpoint);
        response.EnsureSuccessStatusCode(); // Выбросит исключение при ошибке
        return await response.Content.ReadAsStringAsync();
    }
}

Обратите внимание на response.EnsureSuccessStatusCode() — это простой способ проверить, что запрос выполнен успешно (код 2xx).

Практические примеры HTTP запросов на C#

Теперь перейдём к конкретным сценариям. Мы рассмотрим GET и POST запросы, а также работу с заголовками и обработку ошибок.

1. GET-запрос с параметрами

Чтобы передать параметры в строке запроса, можно использовать коллекцию QueryHelpers из Microsoft.AspNetCore.WebUtilities или просто сформировать URL вручную. Ниже пример с ручным формированием:

public async Task<string> SearchUsersAsync(string name, int page)
{
    var query = $"/users?name={Uri.EscapeDataString(name)}&page={page}";
    HttpResponseMessage response = await client.GetAsync(query);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
}

Всегда используйте Uri.EscapeDataString() для экранирования параметров, чтобы избежать ошибок с спецсимволами.

2. POST-запрос с JSON телом

Для отправки данных в формате JSON удобно использовать библиотеку System.Text.Json (или Newtonsoft.Json). Преобразуем объект в JSON и отправим:

using System.Text;
using System.Text.Json;

public async Task<User> CreateUserAsync(User newUser)
{
    var json = JsonSerializer.Serialize(newUser);
    var content = new StringContent(json, Encoding.UTF8, "application/json");

    HttpResponseMessage response = await client.PostAsync("/users", content);
    response.EnsureSuccessStatusCode();

    var responseBody = await response.Content.ReadAsStringAsync();
    return JsonSerializer.Deserialize<User>(responseBody);
}

public class User
{
    public string Name { get; set; }
    public string Email { get; set; }
}

Важно указывать Encoding.UTF8 и правильный MIME-тип (application/json).

3. Работа с заголовками и аутентификацией

Часто требуется добавить кастомные заголовки, например, для авторизации. Лучший способ — установить их в DefaultRequestHeaders или передать через HttpRequestMessage:

public async Task<string> GetAuthenticatedDataAsync(string token)
{
    var request = new HttpRequestMessage(HttpMethod.Get, "/protected/resource");
    request.Headers.Authorization = 
        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
    request.Headers.Add("X-Custom-Header", "myValue");

    HttpResponseMessage response = await client.SendAsync(request);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
}

Метод SendAsync — универсальный, позволяет передать любой HttpRequestMessage с полным контролем.

4. Обработка ошибок и таймаутов

Никогда не забывайте про обработку исключений. Основные типы ошибок:

  • HttpRequestException — ошибка сети или некорректный статус (если вызван EnsureSuccessStatusCode).
  • TaskCanceledException — таймаут запроса.

Пример с настройкой таймаута и обработкой:

client.Timeout = TimeSpan.FromSeconds(10); // Таймаут на весь запрос

public async Task<string> SafeRequestAsync(string url)
{
    try
    {
        HttpResponseMessage response = await client.GetAsync(url);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine($"Ошибка HTTP: {ex.Message}");
        return null;
    }
    catch (TaskCanceledException)
    {
        Console.WriteLine("Запрос превысил таймаут.");
        return null;
    }
}

Заключение: лучшие практики работы с HTTP клиентом в C#

Мы рассмотрели основные аспекты создания HTTP клиента на C# (.NET). Подведём итоги:

  • Используйте один экземпляр HttpClient на всё приложение (или управляйте его жизненным циклом через IHttp

Блоги

Рекомендаций по книгам