Content-Type в C# HttpClient: как настроить и не сломать запрос

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

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

Начать курс

Почему Content-Type — это боль каждого C# разработчика?

Вы когда-нибудь отправляли POST-запрос через HttpClient в C#, а сервер отвечал вам загадочной ошибкой 415 (Unsupported Media Type)? Знакомо, правда? Чаще всего проблема кроется в одном маленьком, но критически важном заголовке — Content-Type. Без него сервер просто не знает, как интерпретировать ваши данные: это JSON, XML или обычная форма?

В этой статье мы разберём, как правильно устанавливать Content-Type для HttpClient в C#, рассмотрим самые частые грабли, на которые наступают новички (и не только), и научимся отправлять запросы так, чтобы сервер вас понял с полуслова. Поехали!

Что такое Content-Type и зачем он нужен?

Content-Type — это HTTP-заголовок, который указывает серверу, в каком формате передаются данные в теле запроса (или ответа). Если вы шлёте JSON, нужно сказать: application/json. Если XML — application/xml. Если данные формы — application/x-www-form-urlencoded.

В C# HttpClient — мощный, но коварный инструмент. Он может сам догадаться о формате, а может и нет. И если вы попытаетесь просто добавить заголовок через DefaultRequestHeaders, вас ждёт неприятный сюрприз: Content-Type не является частью default request headers. Он относится только к телу запроса.

Типичная ошибка новичка

var client = new HttpClient();
client.DefaultRequestHeaders.Add("Content-Type", "application/json"); // ❌ ОШИБКА!

Этот код вызовет исключение InvalidOperationException с сообщением: "Misused header name. Make sure request headers are used with HttpRequestMessage, response ones with HttpResponseMessage, and content ones with HttpContent objects." Звучит пугающе, но решение простое — читайте дальше.

Три способа установить Content-Type в C# HttpClient

Способ 1: Использовать StringContent (самый простой)

Самый популярный и правильный способ — передать заголовок вместе с телом запроса через класс StringContent.

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

class Program
{
    static async Task Main()
    {
        var client = new HttpClient();
        var json = "{ \"name\": \"PythonLib\", \"type\": \"education\" }";
        
        // Создаём контент с явным указанием Content-Type
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        var response = await client.PostAsync("https://api.example.com/data", content);
        Console.WriteLine(await response.Content.ReadAsStringAsync());
    }
}

В этом примере Content-Type: application/json; charset=utf-8 устанавливается автоматически. Второй параметр (Encoding.UTF8) задаёт кодировку, третий — медиатип. Вуаля — сервер доволен!

Способ 2: Через ByteArrayContent (для продвинутых)

Если вы работаете с бинарными данными или хотите полный контроль, используйте ByteArrayContent.

using System.Net.Http;
using System.Text;

var jsonBytes = Encoding.UTF8.GetBytes("{ \"message\": \"Hello\" }");
var content = new ByteArrayContent(jsonBytes);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

var response = await client.PostAsync("https://api.example.com/upload", content);

Здесь мы вручную задаём ContentType через свойство Headers. Этот способ удобен, когда нужно добавить дополнительные заголовки (например, Content-Encoding).

Способ 3: HttpRequestMessage + полный контроль

Иногда нужно отправить GET-запрос с телом (да, так тоже можно) или переопределить заголовки на уровне запроса. Тогда в бой вступает HttpRequestMessage.

using System.Net.Http;
using System.Text;

var request = new HttpRequestMessage(HttpMethod.Post, "https://api.example.com/data");
request.Content = new StringContent("{\"key\":\"value\"}", Encoding.UTF8, "application/json");
// Дополнительные заголовки запроса
request.Headers.Add("Authorization", "Bearer token123");

var response = await client.SendAsync(request);

Важно: Если вы задаёте Content-Type через request.Content.Headers, он переопределит любой заголовок, заданный в StringContent. Приоритет — у контента, который вы присвоили свойству Content.

Частые вопросы (FAQ) по Content-Type в HttpClient

1. Как отправить данные формы (FormUrlEncodedContent)?

Для отправки данных формы не нужно вручную задавать Content-Type — он установится автоматически в application/x-www-form-urlencoded.

var formData = new Dictionary<string, string>
{
    { "username", "admin" },
    { "password", "12345" }
};
var content = new FormUrlEncodedContent(formData);
// Content-Type будет установлен автоматически!
var response = await client.PostAsync("https://api.example.com/login", content);

2. Что делать, если сервер требует конкретный charset?

Используйте перегрузку StringContent с указанием кодировки:

var content = new StringContent(json, Encoding.UTF8, "application/json");
// Результат: Content-Type: application/json; charset=utf-8

Если нужно убрать charset (некоторые старые серверы этого не любят), задайте заголовок вручную:

content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
// Теперь только: Content-Type: application/json

3. Почему мой запрос с JSON всё равно не работает?

Проверьте три вещи:

  • Не пытайтесь задать Content-Type через DefaultRequestHeaders — это вызовет исключение.
  • Убедитесь, что вы используете правильный медиатип: application/json, а не text/json.
  • Если вы используете HttpRequestMessage, не забудьте присвоить свойству Content объект с заголовками.

4. Как отправить XML?

Аналогично JSON, только меняем медиатип:

var xml = "<root><item>value</item></root>";
var content = new StringContent(xml, Encoding.UTF8, "application/xml");

Сравнение подходов: что выбрать?

Метод Когда использовать Сложность
StringContent Стандартные POST/PUT запросы с JSON или XML ⭐ Низкая
ByteArrayContent Бинарные данные, файлы, полный контроль ⭐⭐ Средняя
HttpRequestMessage Сложные сценарии, кастомные заголовки ⭐⭐⭐ Высокая

Прод

Блоги

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