web-dev-qa-db-fra.com

Comment définir l'en-tête Content-Type pour une demande HttpClient?

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 avec HttpResponseMessage et les en-têtes de contenu avec les objets HttpContent.

Comment définir l'en-tête Content-Type dans une requête HttpClient?

633
mynameiscoffey

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);
      });
801
carlosfigueira

Pour ceux qui n'ont pas vu Johns commenter la solution carlos ...

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

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
49
Todd Menier

essayez d'utiliser TryAddWithoutValidation

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

.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.

24
erdomke

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!

16
Jay

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.

15
Robert Levy
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();
    }
}
3
art24war

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

Pour ceux qui ont des ennuis avec 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");
1
deadManN

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

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

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