Как отправить HTTP POST запрос в .NET на C#: от простого к сложному
Представьте, что ваше приложение на C# — это курьер, которому нужно доставить посылку (данные) по определённому адресу (URL) и получить подтверждение (ответ). Именно эту задачу решает HTTP POST запрос. На Stack Overflow этот вопрос собирает тысячи просмотров, и не зря: работа с внешними API, отправка форм, загрузка данных на сервер — это ежедневная рутина любого .NET разработчика.
В этой статье мы разберём три основных способа отправки POST-запроса в .NET (C#), начиная с современного HttpClient и заканчивая «дедовским» WebRequest. Вы узнаете, как передавать JSON, URL-кодированные данные, обрабатывать ошибки и не попадать в ловушки, которые подстерегают новичков. Поехали!
1. Современный подход: HttpClient (рекомендуется)
Начиная с .NET Framework 4.5 и во всех версиях .NET Core / .NET 5+, класс HttpClient является стандартом де-факто. Он спроектирован для асинхронной работы, поддерживает повторное использование соединений и легко настраивается.
1.1. Простой POST с JSON (самый частый сценарий)
Допустим, нам нужно отправить данные пользователя на сервер в формате JSON. Вот как это выглядит в реальном коде:
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
public class UserService
{
private readonly HttpClient _httpClient;
public UserService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<UserResponse> CreateUserAsync(UserDto newUser)
{
// 1. Сериализуем объект в JSON
var json = JsonSerializer.Serialize(newUser);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// 2. Отправляем POST
HttpResponseMessage response = await _httpClient.PostAsync("https://api.example.com/users", content);
// 3. Читаем ответ
response.EnsureSuccessStatusCode(); // Выбросит исключение, если статус не 2xx
var responseBody = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<UserResponse>(responseBody);
}
}
// Пример использования
public class Program
{
public static async Task Main()
{
var client = new HttpClient();
var service = new UserService(client);
var user = new UserDto { Name = "Иван", Email = "ivan@example.com" };
var result = await service.CreateUserAsync(user);
Console.WriteLine($"Создан пользователь ID: {result.Id}");
}
}
public class UserDto
{
public string Name { get; set; }
public string Email { get; set; }
}
public class UserResponse
{
public int Id { get; set; }
}
Важные моменты:
- Всегда используйте
async/await— это не даёт потоку заблокироваться. EnsureSuccessStatusCode()— ваш лучший друг для быстрой проверки ошибок.- Не создавайте новый
HttpClientна каждый запрос — это приводит к socket exhaustion (истощению сокетов). ИспользуйтеIHttpClientFactoryв ASP.NET Core или один статический экземпляр.
1.2. POST с данными формы (application/x-www-form-urlencoded)
Иногда API требует данные в формате, как из обычной HTML-формы. Для этого используем FormUrlEncodedContent:
public async Task<string> PostFormDataAsync(string username, string password)
{
var formData = new Dictionary<string, string>
{
{ "grant_type", "password" },
{ "username", username },
{ "password", password }
};
var content = new FormUrlEncodedContent(formData);
using var client = new HttpClient();
var response = await client.PostAsync("https://api.example.com/token", content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
Этот способ идеален для работы с OAuth2, базовой аутентификацией через форму или отправкой простых полей.
2. Классический способ: HttpWebRequest (для legacy-кода)
Если вы поддерживаете старый проект на .NET Framework 2.0–4.0, вам может встретиться HttpWebRequest. Он более многословен, но всё ещё работает:
public string PostWithWebRequest(string url, string jsonData)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json";
// Записываем тело запроса
byte[] byteArray = Encoding.UTF8.GetBytes(jsonData);
request.ContentLength = byteArray.Length;
using (var dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
}
// Получаем ответ
using (var response = (HttpWebResponse)request.GetResponse())
using (var reader = new StreamReader(response.GetResponseStream()))
{
return reader.ReadToEnd();
}
}
Недостатки: нет встроенной поддержки async/await (нужно писать вручную), нет автоматического управления пулом соединений. Для новых проектов используйте HttpClient.
3. Продвинутые техники: настройка заголовков и таймаутов
Реальные API часто требуют кастомных заголовков: токены авторизации, User-Agent, Content-Encoding. Вот как это сделать правильно:
public async Task<string> PostWithHeadersAsync(string apiKey, object payload)
{
var client = new HttpClient();
// Глобальные настройки
client.BaseAddress = new Uri("https://api.example.com/");
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add("X-API-Key", apiKey);
client.DefaultRequestHeaders.UserAgent.ParseAdd("MyApp/1.0");
var json = JsonSerializer.Serialize(payload);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("v2/data", content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
Совет: если нужно отправить файл (multipart/form-data), используйте MultipartFormDataContent:
public async Task UploadFileAsync(byte[] fileBytes, string fileName)
{
using var form = new MultipartFormDataContent();
form.Add(new ByteArrayContent(fileBytes, 0, fileBytes.Length), "file", fileName);
var response = await _httpClient.PostAsync("upload", form);
// ...
}
4. Обработка ошибок и статус-кодов
Сеть — ненадёжна. Сервер может ответить 404, 500 или просто упасть. Никогда не игнорируйте это:
public async Task<T> SafePostAsync<T>(string url, object data)
{
try
{
var json = JsonSerializer.Serialize(data);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<T>(body);
}
// Логируем ошибку
var errorBody = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Ошибка {response.StatusCode}: {errorBody}");
// Можно выбросить своё исключение
throw new HttpRequestException($"API вернул {response.StatusCode}");
}