J'essaie de définir l'en-tête Content-Type
d'un objet HttpClient
comme l'exige une API que j'appelle.
J'ai essayé de régler le Content-Type
comme ci-dessous:
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");
// ...
}
Cela me permet d'ajouter l'en-tête Accept
, mais lorsque j'essaie d'ajouter Content-Type
, il lève l'exception suivante:
Nom d'en-tête mal utilisé. Assurez-vous que les en-têtes de requête sont utilisés avec
HttpRequestMessage
, les en-têtes de réponse avecHttpResponseMessage
et les en-têtes de contenu avec les objetsHttpContent
.
Comment définir l'en-tête Content-Type
dans une requête HttpClient
?
Le type de contenu est un en-tête du contenu, pas de la demande, c'est pourquoi il échoue. AddWithoutValidation
tel que suggéré par Robert Levy peut fonctionner, mais vous pouvez également définir le type de contenu lors de la création du contenu de la demande (notez que l'extrait de code ajoute "application/json" à deux endroits: pour les en-têtes Accept et 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);
});
Pour ceux qui n'ont pas vu Johns commenter la solution carlos ...
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Si vous ne craignez pas une petite dépendance de bibliothèque, Flurl.Http [divulgation: je suis l'auteur] rend cette procédure extrêmement simple. Sa méthode PostJsonAsync
prend en charge à la fois la sérialisation du contenu et la définition de l'en-tête content-type
, et ReceiveJson
désérialise la réponse. Si l'en-tête accept
est requis, vous devrez le définir vous-même, mais Flurl fournit un moyen relativement simple de le faire:
using Flurl.Http;
var result = await "http://example.com/"
.WithHeader("Accept", "application/json")
.PostJsonAsync(new { ... })
.ReceiveJson<TResult>();
Flurl utilise HttpClient et Json.NET sous le capot, il s’agit d’un fichier PCL et fonctionne donc sur diverses plates-formes.
PM> Install-Package Flurl.Http
essayez d'utiliser TryAddWithoutValidation
var client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
.Net essaie de vous forcer à respecter certaines normes, à savoir que l'en-tête _Content-Type
_ ne peut être spécifié que sur les requêtes ayant un contenu (par exemple, POST
, PUT
, etc.). Par conséquent, comme d'autres l'ont indiqué, le moyen préféré de définir l'en-tête _Content-Type
_ consiste à utiliser la propriété HttpContent.Headers.ContentType
.
Cela dit, certaines API (telles que LiquidFiles Api , à partir du 19/12/2016) nécessitent la définition de l'en-tête _Content-Type
_ pour une demande GET
. .Net n'autorisera pas la définition de cet en-tête sur la demande elle-même - même en utilisant TryAddWithoutValidation
. De plus, vous ne pouvez pas spécifier de Content
pour la demande, même si sa longueur est nulle. La seule façon pour moi de contourner ce problème était de recourir à la réflexion. Le code (au cas où d’autres en auraient besoin) est
_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");
_
Modifier:
Comme indiqué dans les commentaires, ce champ a des noms différents dans différentes versions de la dll. Dans le code source sur GitHub , le champ est actuellement nommé _s_invalidHeaders
_. L'exemple a été modifié pour tenir compte de cela, selon la suggestion de @David Thompson.
Quelques informations supplémentaires sur .NET Core (après avoir lu le message de erdomke sur la définition d'un champ privé pour fournir le type de contenu sur une requête sans contenu) ...
Après le débogage de mon code, je ne vois plus le champ privé à définir via la réflexion. Je me suis donc dit que j'essaierais de recréer le problème.
J'ai essayé le code suivant en utilisant. 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;
Et, comme prévu, je reçois une exception globale avec le contenu "Cannot send a content-body with this verb-type."
Cependant, si je fais la même chose avec .NET Core (1.1) - je ne reçois pas d'exception. Mon application serveur a répondu à ma demande, et le type de contenu a été récupéré. .
J'ai été agréablement surpris par cela et j'espère que cela aidera quelqu'un!
Appelez AddWithoutValidation
au lieu de Add
(voir ce lien MSDN ).
Alternativement, je devine que l'API que vous utilisez ne requiert vraiment cela que pour les demandes POST ou PUT (pas les demandes GET ordinaires). Dans ce cas, lorsque vous appelez HttpClient.PostAsync
et transmettez une HttpContent
, définissez-le sur la propriété Headers
de cet objet HttpContent
.
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"));
C'est tout ce dont vous avez besoin.
Avec Newtonsoft.Json, si vous avez besoin d’un contenu sous forme de chaîne 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, ce n'est pas HTTPClient mais si vous pouvez l'utiliser, WebClient est assez simple:
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(...);
}
charset
J'ai eu un cas très spécial que le fournisseur de services n'a pas accepté charset, et ils refusent de changer la sous-structure pour le permettre ... Malheureusement, HttpClient définissait l'en-tête automatiquement via StringContent, et peu importe si vous transmettez null ou Encoding.UTF8, il définira toujours le jeu de caractères ...
Aujourd'hui, j'étais sur le point de changer de sous-système, passant de HttpClient à un problème qui me venait à l'esprit, pourquoi ne pas utiliser la réflexion pour vider le "jeu de caractères"? ... Et avant même de l'essayer, j'ai trouvé que c'est quelque chose que je peux changer après l'initialisation. Voici comment vous pouvez définir l'en-tête exact "application/json" sans "; 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;
Remarque: La valeur null
dans la suite ne fonctionnera pas et ajoutera "; charset = utf-8"
return new StringContent(jsonRequest, null, "application/json");
Vous pouvez l'utiliser, ce sera un travail!
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();
Je trouve cela le plus simple et facile à comprendre de la manière suivante:
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();
Vous devez le faire comme ceci:
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;