Come si imposta l'intestazione Content-Type per una richiesta HttpClient?


739

Sto cercando di impostare l' Content-Typeintestazione di un HttpClientoggetto come richiesto da un'API che sto chiamando.

Ho provato a impostare Content-Typecome di seguito:

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

Mi consente di aggiungere l' Acceptintestazione ma quando provo ad aggiungerla Content-Typegenera la seguente eccezione:

Nome intestazione abusato. Assicurarsi che vengano utilizzate le intestazioni di richiesta con HttpRequestMessage, le intestazioni di risposta con HttpResponseMessagee le intestazioni di contenuto con HttpContentoggetti.

Come posso impostare l' Content-Typeintestazione in una HttpClientrichiesta?


Puoi seguire come HttpWebRequest in .NET Core lo fa (usa HttpClient internamente), vedi github.com/dotnet/corefx/blob/master/src/System.Net.Requests/… Metodo "SendRequest"
jiping-s

Risposte:


928

Il tipo di contenuto è un'intestazione del contenuto, non della richiesta, motivo per cui ciò non riesce. AddWithoutValidationcome suggerito da Robert Levy potrebbe funzionare, ma puoi anche impostare il tipo di contenuto durante la creazione del contenuto della richiesta stessa (tieni presente che lo snippet di codice aggiunge "application / json" in due posizioni: per le intestazioni 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);
      });

32
In alternativa, Response.Content.Headersfunzionerà per la maggior parte del tempo.
John Gietzen,

4
@AshishJain La maggior parte delle risposte SO che ho visto coinvolgere Response.Content.Headersper l'API Web ASP.Net non ha funzionato neanche, ma puoi facilmente impostarlo usando HttpContext.Current.Response.ContentTypese necessario.
jerhewet,

6
@jerhewet ho usato nel modo seguente che ha funzionato per me. var content = new StringContent (data, Encoding.UTF8, "application / json");
Ashish-BeJovial

22
Content-Type è una proprietà di un messaggio HTTP con payload; non ha nulla a che fare con richiesta vs risposta.
Julian Reschke,

6
Interessante. Ho provato a creare un nuovo StringContent con i tre parametri e non ha funzionato. Ho poi manualmente: request.Content.Headers.Remove ("Content-Type") e poi: request.Content.Headers.Add ("Content-Type", "application / query + json") e ha funzionato. Dispari.
Bill Noel,

163

Per coloro che non hanno visto Johns commentare la soluzione di Carlos ...

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

Ha fatto la differenza scaricando un pdf. Dal telefono ha provato a scaricare un HTML. Dopo aver convertito l'estensione, il file veniva normalmente codificato.
Matteo Defanti,

Ho dovuto lanciare .ToString () alla fine, ma sì, questo ha funzionato per un'implementazione del servizio WCF.
John Meyer,

2
Alla fine capirò quale tipo di oggetto "req" è ... per tentativi ed errori ........ MA sarebbe bello dimostrarlo. Grazie per la vostra considerazione.
granadaCoder

4
Proprio così la gente lo sa, l'uso di MediaTypeHeaderValue restituirà un errore se si tenta di impostare il set di caratteri, in questo modo; response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
MBak,

3
Il commento di Johns alla soluzione di Carlo ha detto Response.Content.Headers, ma stai usando req.Content.Headers? cioè richiesta vs risposta?
Joedotnot non è il

52

Se non ti dispiace per una piccola dipendenza dalla biblioteca, Flurl.Http [divulgazione: sono l'autore] rende tutto super semplice. Il suo PostJsonAsyncmetodo si occupa sia di serializzare il contenuto sia di impostare l' content-typeintestazione e ReceiveJsondeserializza la risposta. Se acceptè richiesta l' intestazione, dovrai impostarla tu stesso, ma Flurl offre anche un modo abbastanza pulito di farlo:

using Flurl.Http;

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

Flurl utilizza HttpClient e Json.NET sotto il cofano, ed è un PCL, quindi funzionerà su una varietà di piattaforme.

PM> Install-Package Flurl.Http

Come inviare se il contenuto è application / x-www-form-urlencoded?
Vlado Pandžić

2
Usato Raggiunto in <1 minuto quello che mi stava prendendo molto tempo per capire. Grazie per mantenere libera questa libreria.
Najeeb,

35

prova ad usare TryAddWithoutValidation

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

4
non funzionante mi dà un 'nome di intestazione abusato. Assicurati che le intestazioni di richiesta vengano utilizzate con HttpRequestMessage, le intestazioni di risposta con HttpResponseMessage e le intestazioni di contenuto con oggetti HttpContent. "
Martin Lietz,

3
Quelli di voi che segnalano "lavorare" o "non lavorare", HttpClient è oggigiorno un oggetto molto ambiguo. Si prega di segnalare il nome completo (spazio) e .dll assembly da cui proviene.
granadaCoder

l' Misused header nameerrore è confermato con dotnet core 2.2. Ho dovuto usare @ risposta di carlosfigueira stackoverflow.com/a/10679340/2084315 .
ps2goat,

funziona per lavori .net completi (4.7).
ZakiMa,

28

.Net cerca di obbligarti a obbedire a determinati standard, vale a dire che l' Content-Typeintestazione può essere specificata solo su richieste che hanno contenuto (ad es POST. PUT, Ecc.). Pertanto, come altri hanno indicato, il modo preferito per impostare l' Content-Typeintestazione è attraverso la HttpContent.Headers.ContentTypeproprietà.

Detto questo, alcune API (come LiquidFiles Api , dal 19-12-2016) richiedono l'impostazione Content-Typedell'intestazione per una GETrichiesta. .Net non consentirà di impostare questa intestazione sulla richiesta stessa, anche utilizzando TryAddWithoutValidation. Inoltre, non è possibile specificare un Contentper la richiesta, anche se è di lunghezza zero. L'unico modo in cui mi sembrava di aggirare questo problema era ricorrere alla riflessione. Il codice (nel caso qualcun altro ne abbia bisogno) è

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

Modificare:

Come notato nei commenti, questo campo ha nomi diversi in diverse versioni della dll. Nel codice sorgente su GitHub , il campo è attualmente denominato s_invalidHeaders. L'esempio è stato modificato per tener conto di questo secondo il suggerimento di @ David Thompson.


1
Questo campo ora è s_invalidHeaders, quindi l'utilizzo di quanto segue garantisce la compatibilità: 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
Grazie, grazie, grazie! A volte il mio mount si blocca e la sbavatura viene fuori quando colpisco un errore API Microsoft. Sono stato in grado di ripulirlo dopo aver visto il tuo post molto semplice. Non troppo male ..
Gerard O'Neill

1
Sono confuso su come questo codice provocherebbe gli errori catastrofici che descrivi. Potete fornire maggiori dettagli sul vostro caso d'uso e sugli errori che state ricevendo?
Erdomke,

2
Wow. Ancora più wow, che i metodi GET di Asp.net WebApi richiedono che Content-Type sia esplicitamente specificato = (
AlfeG

2
Holly Molly, non posso credere di dover ricorrere a questo. Da quando gli sviluppatori di .NET framework hanno bisogno di tenere la mia mano in quello che posso aggiungere alla sezione di intestazione Http? Abominevole.
mmix,

17

Alcune informazioni extra su .NET Core (dopo aver letto il post di erdomke sull'impostazione di un campo privato per fornire il tipo di contenuto su una richiesta che non ha contenuto) ...

Dopo aver eseguito il debug del mio codice, non riesco a vedere il campo privato da impostare tramite reflection, quindi ho pensato di provare a ricreare il problema.

Ho provato il seguente codice utilizzando .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, come previsto, ottengo un'eccezione aggregata con il contenuto "Cannot send a content-body with this verb-type."

Tuttavia, se faccio la stessa cosa con .NET Core (1.1) - non ottengo un'eccezione. La mia richiesta ha ricevuto una risposta abbastanza felice dalla mia applicazione server e il tipo di contenuto è stato raccolto.

Ne sono rimasto piacevolmente sorpreso, e spero che aiuti qualcuno!


1
Grazie, Jay - Non sto usando il core e userò la risposta di Erdomke. Apprezzo sapere che sono stati provati tutti i viali ragionevoli :).
Gerard ONeill,

1
non funzionante .net 4 ({"Impossibile inviare un corpo di contenuto con questo tipo di verbo."})
Tarek El-Mallah,

3
@ TarekEl-Mallah Sì, per favore leggi i commenti nella mia risposta. L'intero punto del mio post è stato quello di illustrare che non funziona in .NET 4, ma funziona in .NET core (non sono la stessa cosa). Dovrai vedere la risposta di Erdomeke alla domanda del PO per essere in grado di hackerarlo per funzionare in .NET 4.
Jay

16

Chiama AddWithoutValidationinvece di Add(vedi questo collegamento MSDN ).

In alternativa, suppongo che l'API che stai utilizzando lo richieda davvero solo per richieste POST o PUT (non richieste GET ordinarie). In tal caso, quando si chiama HttpClient.PostAsynce si passa un HttpContent, impostarlo sulla Headersproprietà HttpContentdell'oggetto.


non funzionante mi dà un 'nome di intestazione abusato. Assicurati che le intestazioni di richiesta vengano utilizzate con HttpRequestMessage, le intestazioni di risposta con HttpResponseMessage e le intestazioni di contenuto con oggetti HttpContent. "
Martin Lietz,

3
AddWithoutValidation non esiste
KansaiRobot

14

Per coloro che hanno avuto problemi charset

Ho avuto un caso molto particolare in cui il fornitore di servizi non ha accettato il set di caratteri e si rifiutano di cambiare la sottostruttura per consentirlo ... Purtroppo HttpClient stava impostando automaticamente l'intestazione tramite StringContent e non importa se si passa null o Encoding.UTF8, imposterà sempre il set di caratteri ...

Oggi ero sul punto di cambiare il sottosistema; passando da HttpClient a qualcos'altro, che qualcosa mi è venuto in mente ..., perché non usare la riflessione per svuotare il "set di caratteri"? ... E prima ancora di provarlo, ho pensato a un modo, "forse posso cambiarlo dopo l'inizializzazione", e ha funzionato.

Ecco come è possibile impostare l'esatta intestazione "application / json" senza "; 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: il nullvalore riportato di seguito non funziona e aggiungi "; charset = utf-8"

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

MODIFICARE

@DesertFoxAZ suggerisce che anche il seguente codice può essere usato e funziona bene. (non l'ho provato io stesso)

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

1
stringContent.Headers.ContentType = new MediaTypeHeaderValue ("application / json"); funziona anche
DesertFoxAZ il

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

È tutto ciò di cui hai bisogno.

Con l'utilizzo di Newtonsoft.Json, se hai bisogno di un contenuto come stringa 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();
    }
}

1
Puoi dare una piccola spiegazione di ciò che fa?
Alejandro,

2
La prima riga non riesce con CS0144: "Impossibile creare un'istanza della classe astratta o interfaccia 'HttpContent'"
Randall Flagg

1
e poiHttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
art24war,

2

Ok, non è HTTPClient ma se puoi usarlo, WebClient è abbastanza semplice:

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

Puoi usarlo, funzionerà!

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

0

Lo trovo molto semplice e facile da capire nel modo seguente:

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

0

Devi farlo in questo modo:

    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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.