Введение: зачем нужен правильный 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