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

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

Ответ 1

Тип содержимого - это заголовок содержимого, а не запроса, поэтому это не удается. 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);
      });

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

.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.

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

var content = new JsonContent();
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"));

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

С использованием Newtonsoft.Json, если вам нужен контент в виде строки json.

public class JsonContent : HttpContent
   {
    private readonly MemoryStream _stream = new MemoryStream();
    ~JsonContent()
    {
        _stream.Dispose();
    }

    public JsonContent(object value)
    {
        Headers.ContentType = new MediaTypeHeaderValue("application/json");
        using (var contexStream = new MemoryStream())
        using (var jw = new JsonTextWriter(new StreamWriter(contexStream)) { Formatting = Formatting.Indented })
        {
            var serializer = new JsonSerializer();
            serializer.Serialize(jw, value);
            jw.Flush();
            contexStream.Position = 0;
            contexStream.WriteTo(_stream);
        }
        _stream.Position = 0;

    }

    private JsonContent(string content)
    {
        Headers.ContentType = new MediaTypeHeaderValue("application/json");
        using (var contexStream = new MemoryStream())
        using (var sw = new StreamWriter(contexStream))
        {
            sw.Write(content);
            sw.Flush();
            contexStream.Position = 0;
            contexStream.WriteTo(_stream);
        }
        _stream.Position = 0;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        return _stream.CopyToAsync(stream);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = _stream.Length;
        return true;
    }

    public static HttpContent FromFile(string filepath)
    {
        var content = File.ReadAllText(filepath);
        return new JsonContent(content);
    }
    public string ToJsonString()
    {
        return Encoding.ASCII.GetString(_stream.GetBuffer(), 0, _stream.GetBuffer().Length).Trim();
    }
}

Ответ 9

Для тех, кто беспокоился о charset

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

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

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

var jsonRequest = JsonSerializeObject(req, options); // Custom function that parse object to string
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");

Ответ 10

Хорошо, это не 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(...);
 }

Ответ 11

Вы можете использовать это, это будет работать!

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

HttpResponseMessage response = await _httpClient.SendAsync(msg);
response.EnsureSuccessStatusCode();

string json = await response.Content.ReadAsStringAsync();

Ответ 12

Вам нужно сделать это так:

    HttpContent httpContent = new StringContent(@"{ the json string }");
    httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));                
    HttpResponseMessage message = client.PostAsync(@"{url}", httpContent).Result;

Ответ 13

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

async Task SendPostRequest()
{
    HttpClient client = new HttpClient();
    var requestContent = new StringContent(<content>);
    requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    var response = await client.PostAsync(<url>, requestContent);
    var responseString = await response.Content.ReadAsStringAsync();
}
...

SendPostRequest().Wait();