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 conHttpResponseMessage
e le intestazioni di contenuto con oggettiHttpContent
.
Come posso impostare l'intestazione Content-Type
in una richiesta HttpClient
?
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);
});
Per coloro che non hanno visto il commento di John alla soluzione di Carlos ...
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
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
prova ad usare TryAddWithoutValidation
var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
.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.
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
.
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!
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();
}
}
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(...);
}