HTTPCLIENT - добавить Content-Type в заголовки [duplicate]

Мы оказываемся во вселенной, которая, по-видимому, развивается по измерению, которое мы называем «временем». Мы не понимаем, какое время, но мы разработали абстракции и словарный запас, которые позволяют рассуждать и говорить об этом: «прошлое», «настоящее», «будущее», «до», «после».

Компьютерные системы, которые мы строим - все больше и больше - имеют время как важное измерение. В будущем будут созданы определенные вещи. Тогда другие вещи должны произойти после того, как эти первые вещи в конечном итоге произойдут. Это основное понятие, называемое «асинхронность». В нашем мире с более сложной сетью наиболее распространенный случай асинхронности ожидает, что какая-то удаленная система ответит на какой-либо запрос.

Рассмотрим пример. Вы называете молочника и заказываете молоко. Когда это произойдет, вы хотите положить его в свой кофе. Вы не можете положить молоко в свой кофе прямо сейчас, потому что его еще нет. Вы должны подождать, пока это произойдет, прежде чем положить его в свой кофе. Другими словами, следующее не будет работать:

var milk = order_milk();
put_in_coffee(milk);

Поскольку JS не знает, что ему нужно дождаться окончания order_milk, прежде чем он выполнит put_in_coffee. Другими словами, он не знает, что order_milk является асинхронным - это то, что не приведет к молоку до некоторого будущего времени. JS и другие декларативные языки, выполняйте один оператор за другим, не ожидая.

Классический подход JS к этой проблеме, используя тот факт, что JS поддерживает функции как объекты первого класса, которые могут быть переданы, заключается в передаче функции в качестве параметра для асинхронного запроса, который затем будет вызываться, когда он будет выполнять свою задачу в будущем. Это подход «обратного вызова». Это выглядит так:

order_milk(put_in_coffee);

order_milk запускает, заказывает молоко, тогда, когда и только когда он прибывает, он вызывает put_in_coffee.

Проблема с этот подход обратного вызова состоит в том, что он загрязняет нормальную семантику функции, сообщающей свой результат с помощью return; вместо этого функции должны сообщать свои результаты, вызывая обратный вызов, заданный как параметр. Кроме того, этот подход может быстро стать громоздким при работе с более длинными последовательностями событий. Например, предположим, что я хочу дождаться, когда молоко будет помещено в кофе, а затем и только затем выполните третий шаг, а именно - выпить кофе. В конце концов мне нужно написать что-то вроде этого:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

, где я перехожу к put_in_coffee как к молоку, чтобы положить в него, так и к действию (drink_coffee), чтобы выполнить как только молоко был введен. Такой код становится трудно писать, читать и отлаживать.

В этом случае мы могли бы переписать код в вопросе как:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

Enter обещает

. Это была мотивация для понятия «обещание», которое является особым типом ценности, представляющим собой будущий или асинхронный результат какого-то рода. Он может представлять что-то, что уже произошло, или это произойдет в будущем, или, возможно, никогда не произойдет вообще. Обещания имеют один метод, названный then, которому вы передаете действие, которое должно быть выполнено, когда был достигнут результат, представленный обещанием.

В случае нашего молока и кофе мы создаем order_milk, чтобы вернуть обещание о прибытии молока, затем укажите put_in_coffee как действие then следующим образом:

order_milk() . then(put_in_coffee)

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

order_milk() . then(put_in_coffee) . then(drink_coffee)

Давайте применим обещания к вашей конкретной проблеме. Мы завершим нашу логику запроса внутри функции, которая возвращает обещание:

function get_data() {
  return $.ajax('/foo.json');
}

На самом деле, все, что мы сделали, добавлено к return к вызову $.ajax. Это работает, потому что jQuery $.ajax уже возвращает вид обетоподобной вещи. (На практике, не вдаваясь в подробности, мы предпочли бы обернуть этот вызов, чтобы вернуть реальное обещание, или использовать некоторую альтернативу $.ajax, которая делает это.) Теперь, если мы хотим загрузить файл и дождаться его завершите, а затем сделайте что-нибудь, мы можем просто сказать

get_data() . then(do_something)

, например,

get_data() . 
  then(function(data) { console.log(data); });

. При использовании обещаний мы заканчиваем передачу множества функций в then, поэтому часто полезно использовать более компактные функции стрелок в стиле ES6:

get_data() . 
  then(data => console.log(data));

Ключевое слово async

Но все еще есть что-то неопределенное в том, что нужно писать код одним способом, если синхронно и совершенно по-другому, если асинхронно. Для синхронного мы пишем

a();
b();

, но если a является асинхронным, с обещаниями мы должны написать

a() . then(b);

Выше, мы сказали: «JS не имеет никакого способа узнать что ему нужно дождаться завершения первого вызова, прежде чем он выполнит второй ». Было бы неплохо, если бы можно было сказать JS? Оказывается, существует ключевое слово await, используемое внутри специального типа функции, называемого функцией «async». Эта функция является частью предстоящей версии ES, но уже доступна в транспилерах, таких как Babel, с учетом правильных настроек. Это позволяет нам просто написать

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

. В вашем случае вы могли бы написать что-то вроде

async function foo() {
  data = await get_data();
  console.log(data);
}
484
задан Nikolai Samteladze 14 May 2015 в 17:36
поделиться

9 ответов

Тип содержимого - это заголовок содержимого, а не запроса, поэтому это не работает. 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);
      });
607
ответ дан Adel Helal 4 September 2018 в 07:00
поделиться
var content = new HttpContent();
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("charset", "utf-8"));
content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("IEEE754Compatible", "true"));

Это все, что вам нужно.

0
ответ дан Alejandro 4 September 2018 в 07:00
поделиться

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

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
106
ответ дан archgl 4 September 2018 в 07:00
поделиться

.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");

Редактировать:

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

14
ответ дан erdomke 4 September 2018 в 07:00
поделиться

Дополнительная информация о .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) - я не получаю исключения , Моя просьба была удовлетворительно удовлетворена моим серверным приложением, и тип контента был поднят.

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

11
ответ дан Jay 4 September 2018 в 07:00
поделиться

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

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

15
ответ дан Keith Pinson 4 September 2018 в 07:00
поделиться

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

  var client = new HttpClient();
  client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
26
ответ дан SharpCoder 4 September 2018 в 07:00
поделиться

Если вы не возражаете против небольшой зависимости от библиотеки, Flurl.Http [раскрытие: я автор] делает этот uber-простой. Его метод 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
38
ответ дан Todd Menier 4 September 2018 в 07:00
поделиться

Хорошо, это не HTTPClient, но если вы можете его использовать, WebClient довольно легко:

using (var client = new System.Net.WebClient())
 {
    client.Headers.Add("Accept", "application/json");
    client.Headers.Add("Content-Type", "application/json; charset=utf-8");
    client.DownloadString(...);
 }
1
ответ дан Ziba Leah 4 September 2018 в 07:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: