Como você define o cabeçalho Content-Type para uma solicitação HttpClient?

739

Estou tentando definir o Content-Typecabeçalho de um HttpClientobjeto, conforme exigido por uma API que estou chamando.

Tentei definir o Content-Typeseguinte abaixo:

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

Ele permite que eu adicione o Acceptcabeçalho, mas quando tento adicioná- Content-Typelo lança a seguinte exceção:

Nome de cabeçalho mal utilizado. Verifique se os cabeçalhos de solicitação são usados ​​com HttpRequestMessage, de resposta HttpResponseMessagee de conteúdo com HttpContentobjetos.

Como posso definir o Content-Typecabeçalho em uma HttpClientsolicitação?

mynameiscoffey
fonte
Você pode acompanhar como o HttpWebRequest no .NET Core faz isso (ele usa o HttpClient internamente), consulte github.com/dotnet/corefx/blob/master/src/System.Net.Requests/… "SendRequest"
jiping-s

Respostas:

928

O tipo de conteúdo é um cabeçalho do conteúdo, não da solicitação, e é por isso que está falhando. AddWithoutValidationconforme sugerido por Robert Levy pode funcionar, mas você também pode definir o tipo de conteúdo ao criar o próprio conteúdo da solicitação (observe que o snippet de código adiciona "application / json" em dois locais - para os cabeçalhos Accept e 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);
      });
carlosfigueira
fonte
32
Como alternativa, Response.Content.Headersfuncionará na maioria das vezes.
John Gietzen
4
@AshishJain A maioria das respostas de SO que eu já vi envolvendo Response.Content.Headerspara a API da Web do ASP.Net também não funcionou, mas você pode configurá-lo facilmente usando, HttpContext.Current.Response.ContentTypese necessário.
jerhewet
6
@jerhewet eu usei da seguinte maneira que funcionou para mim. var content = new StringContent (dados, Encoding.UTF8, "application / json");
Ashish-BeJovial
22
Tipo de conteúdo é uma propriedade de uma mensagem HTTP com carga útil; não tem nada a ver com solicitação versus resposta.
Julian Reschke
6
Interessante. Tentei criar um novo StringContent com os três parâmetros e não funcionou. Em seguida, manualmente: request.Content.Headers.Remove ("Content-Type") e então: request.Content.Headers.Add ("Content-Type", "application / query + json") e funcionou. Ímpar.
Bill Noel
163

Para quem não viu o Johns comentar a solução carlos ...

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
archgl
fonte
Fazia diferença baixar um pdf. No telefone, ele tentou baixar um HTML. Depois de converter a extensão, o arquivo era normalmente codificado.
Matteo Defanti
Eu tive que jogar .ToString () no final, mas sim, isso funcionou para uma implementação de serviço WCF.
John Meyer
2
Acabarei descobrindo que tipo de objeto "req" é ... por tentativa e erro ........ Mas seria ótimo mostrar isso. Obrigado pela sua consideração.
usar o seguinte código
4
Para que as pessoas saibam, o uso do MediaTypeHeaderValue retornará um erro se você tentar definir o conjunto de caracteres, assim; response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
MBak 18/01/19
3
O comentário de Johns à solução de Carlo disse Response.Content.Headers, mas você está usando req.Content.Headers? ie Solicitação vs Resposta?
precisa saber é
52

Se você não se importa com uma pequena dependência de biblioteca, o Flurl.Http [divulgação: eu sou o autor] torna isso extremamente simples. Seu PostJsonAsyncmétodo cuida da serialização do conteúdo e da configuração do content-typecabeçalho e ReceiveJsondesserializa a resposta. Se o acceptcabeçalho for necessário, você precisará definir isso sozinho, mas o Flurl também oferece uma maneira bastante limpa de fazer isso:

using Flurl.Http;

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

O Flurl usa o HttpClient e o Json.NET sob o capô, e é um PCL para funcionar em várias plataformas.

PM> Install-Package Flurl.Http
Todd Menier
fonte
Como enviar se o conteúdo é application / x-www-form-urlencoded?
Vlado Pandžić
2
Usou. Consegui em <1 minuto o que estava demorando muito tempo para descobrir. Obrigado por manter esta biblioteca gratuita.
Najeeb
35

tente usar TryAddWithoutValidation

  var client = new HttpClient();
  client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
SharpCoder
fonte
4
não funcionar me fornece um nome de cabeçalho mal utilizado. Verifique se os cabeçalhos de solicitação são usados ​​com HttpRequestMessage, os cabeçalhos de resposta com HttpResponseMessage e os cabeçalhos de conteúdo com os objetos HttpContent. '
Martin Lietz
3
Aqueles de vocês que relatam "trabalhando" ou "não estão funcionando", HttpClient, são um objeto muito ambíguo atualmente. Por favor, reporte o nome completo (espaço) e o assembly .dll de onde ele vem.
usar o seguinte código
o Misused header nameerro é confirmado com o dotnet core 2.2. Eu tive que usar a resposta de @ carlosfigueira stackoverflow.com/a/10679340/2084315 .
ps2goat 5/02/19
ele funciona para trabalhos .net completos (4.7).
ZakiMa
28

.Net tenta forçá-lo a obedecer a certas normas, ou seja, que o Content-Typecabeçalho só pode ser especificada em pedidos que tenham conteúdo (por exemplo POST, PUT, etc.). Portanto, como outros indicaram, a maneira preferida de definir o Content-Typecabeçalho é através da HttpContent.Headers.ContentTypepropriedade

Com isso dito, determinadas APIs (como a LiquidFiles Api , em 19/12/2016) exigem a configuração do Content-Typecabeçalho para uma GETsolicitação. .Net não permitirá definir esse cabeçalho na própria solicitação - mesmo usando TryAddWithoutValidation. Além disso, você não pode especificar a Contentpara a solicitação - mesmo que seja de comprimento zero. A única maneira que eu conseguia contornar isso era recorrer à reflexão. O código (no caso de alguém precisar dele) é

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

Editar:

Conforme observado nos comentários, esse campo tem nomes diferentes em diferentes versões da DLL. No código fonte do GitHub , o campo está atualmente nomeado s_invalidHeaders. O exemplo foi modificado para explicar isso de acordo com a sugestão de @David Thompson.

erdomke
fonte
1
Este campo agora é s_invalidHeaders, portanto, o seguinte garante a compatibilidade: 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);
David Thompson
2
Obrigado, obrigado, obrigado! Às vezes, minha montagem fica aberta e a baba sai quando eu bato em uma falha da API da Microsoft. Consegui limpá-la depois de ver sua postagem muito direta. Não muito ruim ..
Gerard ONeill
1
Estou confuso sobre como esse código causaria os erros catastróficos que você descreve. Você pode fornecer mais detalhes sobre seu caso de uso e os erros que está recebendo?
erdomke
2
Uau. Ainda mais uau, que os métodos Asp.net WebApi GET exigem que o Tipo de conteúdo seja especificado explicitamente = (
AlfeG
2
Holly Molly, não acredito que tenho que recorrer a isso. Desde quando o .NET framework devs é necessário segurar minha mão no que posso adicionar à seção de cabeçalho Http? Abominável.
MMIX
17

Algumas informações adicionais sobre o .NET Core (depois de ler a postagem de erdomke sobre como definir um campo privado para fornecer o tipo de conteúdo em uma solicitação que não possui conteúdo) ...

Depois de depurar meu código, não consigo ver o campo privado para definir via reflexão - então pensei em tentar recriar o problema.

Eu tentei o seguinte código usando o .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;

E, como esperado, recebo uma exceção agregada com o conteúdo "Cannot send a content-body with this verb-type."

No entanto, se eu fizer a mesma coisa com o .NET Core (1.1) - não recebo uma exceção. Minha solicitação foi atendida com satisfação pelo meu aplicativo de servidor e o tipo de conteúdo foi escolhido.

Fiquei agradavelmente surpreso com isso, e espero que ajude alguém!

Jay
fonte
1
Obrigado, Jay - Não use o core, e usará a resposta de erdomke. Aprecio saber que todos os caminhos razoáveis ​​foram tentados :).
precisa
1
.net não está funcionando 4 ({ "Não é possível enviar um conteúdo de corpo com este tipo de verbo."})
Tarek El-Mallah
3
@ TarekEl-Mallah Sim - por favor, leia os comentários na minha resposta. O objetivo principal da minha postagem foi ilustrar que ele não funciona no .NET 4, mas funciona no núcleo do .NET (eles não são a mesma coisa). Você terá que ver a resposta de erdomeke à pergunta do OP para poder fazer com que ele funcione no .NET 4. #
Jay Jay
16

Ligue em AddWithoutValidationvez deAdd (consulte este link do MSDN ).

Como alternativa, acho que a API que você está usando realmente exige isso apenas para solicitações POST ou PUT (não solicitações GET comuns). Nesse caso, quando você chamar HttpClient.PostAsynce passar um HttpContent, defina isso na Headerspropriedade desse HttpContentobjeto.

Robert Levy
fonte
não funcionar me fornece um nome de cabeçalho mal utilizado. Verifique se os cabeçalhos de solicitação são usados ​​com HttpRequestMessage, os cabeçalhos de resposta com HttpResponseMessage e os cabeçalhos de conteúdo com os objetos HttpContent. '
Martin Lietz 4/18
3
Não AddWithoutValidation não existe
KansaiRobot
14

Para aqueles que se preocuparam com charset

Eu tive um caso muito especial de que o provedor de serviços não aceitava charset e eles se recusam a alterar a subestrutura para permitir isso ... Infelizmente, o HttpClient estava configurando o cabeçalho automaticamente por meio de StringContent, e não importa se você passa nulo ou Encoding.UTF8, sempre definirá o conjunto de caracteres ...

Hoje eu estava no limite para mudar o subsistema; passando de HttpClient para qualquer outra coisa, que algo veio à minha mente ..., por que não usar a reflexão para esvaziar o "conjunto de caracteres"? ... E antes mesmo de tentar, pensei em uma maneira "talvez eu possa alterá-lo após a inicialização", e funcionou.

Veja como você pode definir o cabeçalho "application / json" exato sem "; 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;

Nota: O nullvalor a seguir não funcionará e acrescente "; charset = utf-8"

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

EDITAR

O @DesertFoxAZ sugere que também o código a seguir pode ser usado e funciona bem. (eu mesmo não testei)

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
deadManN
fonte
1
stringContent.Headers.ContentType = new MediaTypeHeaderValue ("application / json"); também funciona
DesertFoxAZ 16/04
4
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"));

É tudo o que você precisa.

Com o uso do Newtonsoft.Json, se você precisar de um conteúdo como json string.

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();
    }
}
art24war
fonte
1
Você pode dar uma pequena explicação sobre o que faz?
Alejandro
2
A primeira linha falha com CS0144: "Não é possível criar uma instância da classe abstrata ou da interface 'HttpContent'" "
Randall Flagg
1
e depoisHttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
art24war
2

Ok, não é HTTPClient, mas se você pode usá-lo, o WebClient é bastante fácil:

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(...);
 }
Ziba Leah
fonte
1

Você pode usar isso, será trabalho!

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();
Kumaran
fonte
0

Acho mais simples e fácil de entender da seguinte maneira:

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();
Anshuman Goel
fonte
0

Você precisa fazer assim:

    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;
user890332
fonte