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

354

Я пытаюсь установить заголовок 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?

Теги:
rest
http

7 ответов

460
Лучший ответ

Тип содержимого - это заголовок содержимого, а не запроса, поэтому это не работает. AddWithoutValidation, как предложил Роберт Леви, может работать, но вы также можете использовать тип содержимого при создании самого содержимого запроса (обратите внимание, что фрагмент кода добавляет "application/json" в двух местах - для заголовков Accept и Content-Type):

HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://example.com/");
client.DefaultRequestHeaders
      .Accept
      .Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "relativeAddress");
request.Content = new StringContent("{\"name\":\"John Doe\",\"age\":33}",
                                    Encoding.UTF8, 
                                    "application/json");//CONTENT-TYPE header

client.SendAsync(request)
      .ContinueWith(responseTask =>
      {
          Console.WriteLine("Response: {0}", responseTask.Result);
      });
  • 24
    Альтернативно, Response.Content.Headers будет работать большую часть времени.
  • 2
    @AshishJain Большинство ответов SO, которые я видел, включая Response.Content.Headers для ASP.Net Web API, также не сработали, но вы можете легко установить его, используя HttpContext.Current.Response.ContentType если вам нужно.
Показать ещё 13 комментариев
88

Для тех, кто не видел комментария Джонса к решению carlos...

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
  • 0
    Это имело значение, загружая PDF. С телефона пытался скачать HTML. После преобразования расширения файл был нормально закодирован.
  • 0
    Мне пришлось бросить .ToString () в конце, но да, это сработало для реализации службы WCF.
Показать ещё 4 комментария
31

Если вы не против небольшой зависимости от библиотеки, Flurl.Http [раскрытие: я автор] делает этот uber-simple, Его метод PostJsonAsync обеспечивает как сериализацию содержимого, так и установку заголовка content-type, а ReceiveJson - десериализацию ответа. Если требуется заголовок accept, вам нужно будет установить его самостоятельно, но Flurl также предоставляет довольно чистый способ:

using Flurl.Http;

var result = await "http://example.com/"
    .WithHeader("Accept", "application/json")
    .PostJsonAsync(new { ... })
    .ReceiveJson<TResult>();

Flurl использует HttpClient и Json.NET под капотом, и это PCL, поэтому он будет работать на самых разных платформах.

PM> Install-Package Flurl.Http
  • 0
    Как отправить, если контент является application / x-www-form-urlencoded?
  • 0
    @Vlado использовать PostUrlEncodedAsync . tmenier.github.io/Flurl/fluent-http
Показать ещё 2 комментария
20

попытайтесь использовать TryAddWithoutValidation

  var client = new HttpClient();
  client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
  • 13
    не работает....
  • 2
    не работает дает мне 'Неправильное имя заголовка. Убедитесь, что заголовки запросов используются с HttpRequestMessage, заголовки ответов с HttpResponseMessage и заголовки содержимого с объектами HttpContent. '
Показать ещё 4 комментария
12

Вызовите AddWithoutValidation вместо Add (см. эта ссылка MSDN).

В качестве альтернативы, я предполагаю, что API, который вы используете, действительно требует только этого для запросов POST или PUT (не обычные запросы GET). В этом случае, когда вы вызываете HttpClient.PostAsync и передаете в HttpContent, установите это в свойстве Headers этого объекта HttpContent.

  • 2
    AddWithoutValidation выдает ту же ошибку
  • 0
    не работает ....
Показать ещё 2 комментария
11

.Net пытается заставить вас подчиняться определенным стандартам, а именно, что заголовок Content-Type может быть указан только в запросах, имеющих контент (например, POST, PUT и т.д.). Поэтому, как указывали другие, предпочтительный способ установки заголовка Content-Type заключается в свойстве HttpContent.Headers.ContentType.

При этом некоторые API (например, LiquidFiles Api, начиная с 2016-12-19) требуют установки заголовка Content-Type для запроса GET..Net не позволит устанавливать этот заголовок на самом запросе - даже используя TryAddWithoutValidation. Кроме того, вы не можете указать Content для запроса - даже если он имеет нулевую длину. Единственный способ, которым я мог бы обойти это, - прибегнуть к размышлениям. Код (в случае, если ему это еще нужно) -

var field = typeof(System.Net.Http.Headers.HttpRequestHeaders)
    .GetField("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) 
  ?? typeof(System.Net.Http.Headers.HttpRequestHeaders) 
    .GetField("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
if (field != null)
{
  var invalidFields = (HashSet<string>)field.GetValue(null);
  invalidFields.Remove("Content-Type");
}
_client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/xml");

Edit:

Как отмечено в комментариях, это поле имеет разные имена в разных версиях dll. В исходном коде на GitHub поле в настоящее время называется s_invalidHeaders. Пример был изменен для учета этого в предположении @David Thompson.

  • 0
    Не работает с .Net Framework версии 4.0, System.Net.Http версии 2.2.29.0, но работает с 2.0.0.0
  • 1
    Это поле теперь s_invalidHeaders, поэтому использование следующего обеспечивает совместимость: var field = typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static) ?? typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
Показать ещё 7 комментариев
7

Дополнительная информация о .NET Core (после прочтения сообщения erdomke о настройке частного поля для предоставления типа содержимого в запросе, который не имеет содержимого)...

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

Я пробовал следующий код, используя .Net 4.6:

HttpRequestMessage httpRequest = new HttpRequestMessage(HttpMethod.Get, @"myUrl");
httpRequest.Content = new StringContent(string.Empty, Encoding.UTF8, "application/json");

HttpClient client = new HttpClient();
Task<HttpResponseMessage> response =  client.SendAsync(httpRequest);  //I know I should have used async/await here!
var result = response.Result;

И, как и ожидалось, я получаю сводное исключение с содержимым "Cannot send a content-body with this verb-type."

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

Я был приятно удивлен этим, и я надеюсь, что это поможет кому-то!

  • 1
    Спасибо, Джей - Не использую ядро, и буду использовать ответ Эрдомке. Я ценю знание того, что все разумные пути были опробованы :).
  • 0
    отлично работает в .NET Core, спасибо!
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню