it-swarm-eu.dev

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

Sto cercando di impostare l'intestazione Content-Type di un oggetto HttpClient come richiesto da un'API che sto chiamando.

Ho provato a impostare Content-Type come 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 permette di aggiungere l'intestazione Accept ma quando provo ad aggiungere Content-Type si genera la seguente eccezione:

Nome dell'intestazione misused. Assicurati che le intestazioni di richiesta siano utilizzate con HttpRequestMessage, le intestazioni di risposta con HttpResponseMessage e le intestazioni di contenuto con oggetti HttpContent.

Come posso impostare l'intestazione Content-Type in una richiesta HttpClient?

588
mynameiscoffey

Il tipo di contenuto è un'intestazione del contenuto, non della richiesta, motivo per cui questo non funziona. AddWithoutValidation come suggerito da Robert Levy potrebbe funzionare, ma puoi anche impostare il tipo di contenuto quando crei il contenuto della richiesta stessa (nota che lo snippet di codice aggiunge "application/json" in due punti, 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);
      });
741
carlosfigueira

Per coloro che non hanno visto il commento di John alla soluzione di Carlos ...

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

Se non ti dispiace una dipendenza da una piccola libreria, Flurl.Http [disclosure: I'm the author] rende questo super-semplice. Il suo metodo PostJsonAsync si occupa sia della serializzazione del contenuto che dell'impostazione dell'intestazione content-type, mentre ReceiveJson deserializza la risposta. Se è richiesta l'intestazione accept, dovrai impostarla tu stesso, ma Flurl fornisce un modo abbastanza semplice per farlo:

using Flurl.Http;

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

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

PM> Install-Package Flurl.Http
47
Todd Menier

prova ad usare TryAddWithoutValidation

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

.Net cerca di forzare l'utente a rispettare determinati standard, ovvero che l'intestazione Content-Type può essere specificata solo su richieste con contenuto (ad esempio POST, PUT, ecc.). Pertanto, come altri hanno indicato, il modo preferito per impostare l'intestazione Content-Type è attraverso la proprietà HttpContent.Headers.ContentType .

Detto questo, alcune API (come ad esempio LiquidFiles Api , a partire dal 2016-12-19) richiedono l'impostazione dell'intestazione Content-Type per una richiesta GET. .Net non consentirà di impostare questa intestazione sulla richiesta stessa - anche usando TryAddWithoutValidation. Inoltre, non è possibile specificare una Content per la richiesta, anche se è di lunghezza zero. L'unico modo in cui potevo aggirare questo era ricorrere alla riflessione. Il codice (nel caso ce ne sia bisogno qualcun altro) lo è

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

Modifica:

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

21
erdomke

Chiama AddWithoutValidation invece di Add (vedi questo link MSDN ).

In alternativa, suppongo che l'API che stai usando richieda realmente questo solo per POST o richieste PUT (non richieste GET ordinarie). In tal caso, quando chiami HttpClient.PostAsync e passi in un HttpContent, imposta questo nella proprietà Headers dell'oggetto HttpContent.

15
Robert Levy

Alcune informazioni aggiuntive 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 la riflessione, quindi ho pensato di provare a ricreare il problema.

Ho provato il seguente codice usando .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 è stata soddisfacentemente soddisfatta dalla mia applicazione server, e il tipo di contenuto è stato raccolto.

Sono rimasto piacevolmente sorpreso da questo, e spero che aiuti qualcuno!

14
Jay
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 è necessario 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();
    }
}
3
art24war

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