HTTP клиент на C#: делаем запросы правильно от А до Я
В современной разработке на платформе .NET работа с HTTP-запросами — одна из ключевых задач. Будь то интеграция с внешним REST API, отправка данных на сервер или загрузка веб-страниц — правильное использование HttpClient в C# критически важно для производительности и стабильности приложения. В этой статье мы разберём все аспекты работы с HTTP клиентом на C#: от базовых примеров до продвинутых техник, обработки ошибок и лучших практик.
1. Основы работы с HttpClient в C#
HttpClient — это основной класс в .NET для отправки HTTP-запросов и получения ответов. Он находится в пространстве имён System.Net.Http и доступен начиная с .NET Framework 4.5 и всех версий .NET Core/.NET 5+.
Важное правило: HttpClient предназначен для повторного использования. Создавать новый экземпляр на каждый запрос — антипаттерн, который может привести к исчерпанию сокетов (socket exhaustion). Вместо этого используйте один статический или синглтон-экземпляр.
Базовый пример GET-запроса:
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
private static readonly HttpClient client = new HttpClient();
static async Task Main(string[] args)
{
try
{
HttpResponseMessage response = await client.GetAsync("https://api.example.com/data");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
catch (HttpRequestException e)
{
Console.WriteLine($"Ошибка запроса: {e.Message}");
}
}
}
В этом примере мы создаём статический HttpClient, отправляем GET-запрос, проверяем успешность ответа с помощью EnsureSuccessStatusCode() и читаем тело ответа как строку.
2. Типы HTTP-запросов и работа с заголовками
Помимо GET, HttpClient поддерживает все основные HTTP-методы: POST, PUT, DELETE, PATCH и другие. Рассмотрим отправку POST-запроса с JSON-данными — это наиболее частый сценарий при работе с REST API.
Пример POST-запроса с JSON:
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
private static readonly HttpClient client = new HttpClient();
static async Task Main(string[] args)
{
var data = new { Name = "John", Age = 30 };
string json = JsonSerializer.Serialize(data);
var content = new StringContent(json, Encoding.UTF8, "application/json");
try
{
HttpResponseMessage response = await client.PostAsync("https://api.example.com/users", content);
response.EnsureSuccessStatusCode();
string result = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Создан пользователь: {result}");
}
catch (HttpRequestException e)
{
Console.WriteLine($"Ошибка: {e.Message}");
}
}
}
Работа с заголовками: Часто API требуют передачи токенов аутентификации или других заголовков. Добавлять их можно двумя способами:
- Глобально — через свойство
DefaultRequestHeaders(для всех запросов клиента). - Локально — через коллекцию
Headersв объектеHttpRequestMessage(для конкретного запроса).
Пример добавления заголовка Authorization:
// Глобально
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "your-token");
// Локально
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/protected");
request.Headers.Add("X-Custom-Header", "value");
var response = await client.SendAsync(request);
3. Обработка ошибок, таймауты и повторные попытки
Реальные приложения должны быть устойчивы к сетевым проблемам. Рассмотрим три ключевых аспекта правильной работы с HTTP клиентом на C#.
3.1. Таймауты
По умолчанию HttpClient имеет таймаут 100 секунд. Это слишком много для большинства сценариев. Устанавливайте разумные таймауты через свойство Timeout:
client.Timeout = TimeSpan.FromSeconds(30); // 30 секунд
3.2. Обработка ошибок
Не все ошибки — это исключения. HTTP-статусы 4xx и 5xx не вызывают исключений автоматически. Используйте EnsureSuccessStatusCode() или проверяйте IsSuccessStatusCode вручную:
if (response.IsSuccessStatusCode)
{
// Успех
}
else
{
Console.WriteLine($"Ошибка: {response.StatusCode}");
string errorBody = await response.Content.ReadAsStringAsync();
}
3.3. Политика повторных попыток (Retry)
Для временных ошибок (например, 503 Service Unavailable или таймаутов) используйте библиотеку Polly или реализуйте простой retry-механизм:
using Polly;
using Polly.Retry;
private static readonly AsyncRetryPolicy retryPolicy =
Policy.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => (int)r.StatusCode >= 500)
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
// Использование
HttpResponseMessage response = await retryPolicy.ExecuteAsync(() =>
client.GetAsync("https://api.example.com/data"));
4. Продвинутые техники: потоковая передача и отмена запросов
Для работы с большими файлами или длинными ответами используйте потоковую передачу, чтобы не загружать всё в память:
using (var stream = await response.Content.ReadAsStreamAsync())
{
// Читаем поток, например, сохраняем в файл
using (var fileStream = File.Create("largefile.dat"))
{
await stream.CopyToAsync(fileStream);
}
}
Отмена запросов через CancellationToken критична для UI-приложений и микросервисов:
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); // таймаут для запроса try { var response = await client.GetAsync("https://api.example.com", cts.Token); // Обработка } catch (OperationCanceledException) { Console.WriteLine("Запрос был отменён или превышен таймаут"); }Блоги
Рекомендаций по книгам
Читайте также