Отправляем HTTP POST в .NET: от новичка до профи на C#

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

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

Начать курс

Как отправить 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}");
    }                

Блоги

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