11

Как установить заголовок Content-Type для запроса HttpClient?

14

Я пытаюсь установить заголовок Content-Type для объекта HttpClient в соответствии с требованиями API, к которому я обращаюсь.

Я пробовал установить Content-Type следующим образом:

using (var httpClient = new HttpClient())
{
    httpClient.BaseAddress = new Uri("http://example.com/");
    httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
    httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
    // ...
}

Мне удалось добавить заголовок Accept, но когда я пытаюсь добавить Content-Type, возникает следующее исключение:

Неправильно использованное имя заголовка. Убедитесь, что заголовки запроса используются с HttpRequestMessage, заголовки ответа — с HttpResponseMessage, а заголовки содержимого — с объектами HttpContent.

Как я могу установить заголовок Content-Type в запросе HttpClient?

5 ответ(ов)

2

В вашем коде вы устанавливаете заголовок Content-Type для содержимого запроса req. Это делается с помощью установки ContentType в MediaTypeHeaderValue, что позволяет указать тип передаваемых данных. В данном случае вы задаёте Content-Type как application/octet-stream, что указывает на то, что данные представляют собой неструктурированный двоичный поток. Это может быть полезно, если вы отправляете файл или данные, которые не относятся к определённому типу (например, текстовые или мультимедийные).

Пример на русском как ответ на вопрос:

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

В этом коде вы устанавливаете заголовок Content-Type для содержимого вашего HTTP-запроса на application/octet-stream, что сигнализирует серверу о том, что вы передаёте двоичные данные.

0

Вы можете использовать метод TryAddWithoutValidation для добавления заголовков в DefaultRequestHeaders вашего HttpClient без проверки на соответствие стандартам. Это особенно полезно, если вы хотите избежать возможных исключений, которые могут возникнуть, если заголовок не соответствует ожидаемым форматам.

Пример кода на C#:

var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");

В этом примере мы создаем новый экземпляр HttpClient, а затем добавляем заголовок Content-Type, указывающий, что содержимое будет в формате JSON с кодировкой UTF-8. Использование TryAddWithoutValidation гарантирует, что ваш код не вызовет исключение, даже если заголовок не соответствует стандартам HTTP, однако будьте осторожны, так как это может привести к проблемам с совместимостью в некоторых случаях.

0

Для тех, кто столкнулся с проблемой charset

У меня была очень специфическая ситуация, когда провайдер услуг не принимал charset и отказался изменять подструктуру, чтобы это разрешить... К сожалению, HttpClient автоматически устанавливает заголовок через StringContent, и неважно, передадите ли вы null или Encoding.UTF8, он всегда будет добавлять charset...

Сегодня я был на грани того, чтобы сменить подсистему; перейти с HttpClient на что-то другое, когда мне пришла в голову мысль... Почему бы не использовать рефлексию, чтобы очистить "charset"? ... И прежде чем я даже попробовал это сделать, я придумал способ: "может быть, я смогу изменить это после инициализации", и это сработало.

Вот как вы можете установить заголовок "application/json" без "; charset=utf-8".

var jsonRequest = JsonSerializeObject(req, options); // Пользовательская функция, которая сериализует объект в строку
var stringContent = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
stringContent.Headers.ContentType.CharSet = null;
return stringContent;

Примечание: Значение null в следующем случае не сработает и добавит "; charset=utf-8"

return new StringContent(jsonRequest, null, "application/json");

EDIT

@DesertFoxAZ предлагает, что также может быть использован следующий код, который работает отлично. (Я сам не тестировал это, если это сработает, оцените и дайте ему кредит в комментариях)

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

EDIT 2

.NET 8 (или, возможно, все версии .NET или даже .NET Core)

Как объяснил @EionRobb, новая версия .NET ведет себя не так, как старая, установка stringContent.Headers.ContentType в null является решением, и new MediaTypeHeaderValue("application/json") может не сработать. Только что обновлено, не тестировалось мной.

0

Ответ на этот вопрос заключается в том, что вы можете добавлять различные заголовки к вашему запросу, используя код вроде следующего:

HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("Accept-Language", "en"); // работает нормально

Однако не все заголовки можно установить таким образом. Например, если вы попробуете добавить заголовок Content-Type следующим образом:

request.Headers.Add("Content-Type", "application/json"); // ошибка

то получите исключение времени выполнения Misused header name. Может показаться, что следующий код сработает:

request.Headers.Add(
   HttpRequestHeader.ContentType.ToString(), // бесполезно
   "application/json"
);

Но это приведет к созданию бесполезного заголовка с именем ContentType, без дефиса. Хотя имена заголовков не чувствительны к регистру, они очень чувствительны к наличию дефисов.

Решение состоит в том, чтобы определить кодировку и тип тела при добавлении содержимого к части HTTP-запроса:

string Body = "...";
request.Content = new StringContent(Body, Encoding.UTF8, "application/json");

Только после этого соответствующий HTTP-заголовок будет автоматически добавлен к запросу, например:

Content-Type: application/json; charset=utf-8

Мне было сложно это выяснить, используя Fiddler на машине без прокси-сервера. В Visual Studio раньше был инструмент сети, который позволял просматривать все заголовки, но он присутствовал только в версии 2015, а в более новых версиях 2017 и 2022 его нет. Если вы используете отладчик для проверки request.Headers, вы не найдете заголовок, который был добавлен автоматически с помощью StringContent().

0

Похоже, вы столкнулись с различиями в обработке HTTP-запросов между .NET Framework и .NET Core. В вашем коде вы пытаетесь отправить запрос GET с содержимым, что является некорректным, поскольку по стандарту HTTP метод GET не должен иметь тела запроса.

Когда вы используете .NET Framework 4.6, вы правильно получаете исключение AggregateException, потому что он жестко соблюдает данный стандарт и не позволяет отправлять тело с запросом GET.

Однако в .NET Core (начиная с версии 1.1) поведение отличается. Может показаться, что .NET Core менее строгий в этой ситуации. Это связано с различиями в реализации HttpClient и в том, как он обрабатывает HTTP-запросы. В .NET Core не происходит бросания исключения, и сервер может обработать ваш запрос, несмотря на то, что он не соответствует стандарту. В этом случае сервер принимает ваш запрос, и вы получаете ответ, но это не является корректным с точки зрения стандартов HTTP.

Рекомендуется, чтобы вы избегали отправки содержимого в запросах GET, так как это может привести к непредсказуемому поведению в различных средах и с различными серверами. Если вам действительно нужно передать данные на сервер, лучше использовать метод POST или другой подходящий метод, который допускает наличие тела запроса.

Вот как вы можете изменить ваш код:

HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Post, @"myUrl");
httpRequest.Content = new StringContent("Ваши данные", Encoding.UTF8, "application/json");

HttpClient client = new HttpClient();
Task<HttpResponseMessage> response =  client.SendAsync(httpRequest);  // Можно использовать async/await!
var result = response.Result;

Таким образом, вы будете следовать стандартам и избежать подобных проблем в будущем.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь