Jak ustawić nagłówek Content-Type dla żądania HttpClient?


739

Próbuję ustawić Content-Typenagłówek HttpClientobiektu zgodnie z wymaganiami wywoływanego interfejsu API.

Próbowałem ustawić Content-Typeponiżej:

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

Pozwala mi to dodać Acceptnagłówek, ale gdy próbuję go dodać Content-Type, zgłasza następujący wyjątek:

Niewłaściwie użyta nazwa nagłówka. Upewnij się, że używane są nagłówki żądania, nagłówki HttpRequestMessageodpowiedzi z HttpResponseMessageoraz nagłówki zawartości z HttpContentobiektami.

Jak ustawić Content-Typenagłówek w HttpClientżądaniu?


Możesz śledzić, w jaki sposób robi to HttpWebRequest w .NET Core (używa HttpClient wewnętrznie), patrz github.com/dotnet/corefx/blob/master/src/System.Net.Requests/ ... Metoda „ SendRequest ”
— jiping-s

Odpowiedzi:


928

Typ zawartości jest nagłówkiem treści, a nie żądania, dlatego to się nie udaje. AddWithoutValidationzgodnie z sugestią Roberta Levy'ego może działać, ale można również ustawić typ zawartości podczas tworzenia treści żądania (pamiętaj, że fragment kodu dodaje „application / json” w dwóch miejscach - dla nagłówków Accept i 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
Alternatywnie Response.Content.Headersbędzie działać przez większość czasu.
— John Gietzen,

4
@AshishJain Większość odpowiedzi SO, które widziałem związane Response.Content.Headersz interfejsem API sieci Web ASP.Net, również nie działała, ale możesz je łatwo ustawić, HttpContext.Current.Response.ContentTypejeśli zajdzie taka potrzeba.
— jerhewet

6
@jerhewet użyłem w następujący sposób, który działał dla mnie. var content = new StringContent (data, Encoding.UTF8, „application / json”);
— Ashish-BeJovial

22
Content-Type jest właściwością wiadomości HTTP z ładunkiem; nie ma to nic wspólnego z żądaniem a odpowiedzią.
— Julian Reschke

6
Ciekawy. Próbowałem utworzyć nowy obiekt StringContent z trzema parametrami i nie działał. Następnie ręcznie: request.Content.Headers.Remove („Content-Type”), a następnie: request.Content.Headers.Add („Content-Type”, „application / query + json”) i zadziałało. Dziwny.
— Bill Noel

163

Dla tych, którzy nie widzieli Johnsa komentarza do rozwiązania Carlos ...

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

Pobranie pliku pdf miało znaczenie. Z telefonu próbował pobrać kod HTML. Po konwersji rozszerzenia plik normalnie był kodowany.
— Matteo Defanti

Na końcu musiałem wyrzucić .ToString (), ale tak, działało to w przypadku implementacji usługi WCF.
— John Meyer,

2
W końcu dowiem się, jaki jest typ obiektu „req” ... metodą prób i błędów ........ ALE fajnie byłoby to pokazać. Dziękuję za uwagę.
— granadaCoder,

4
Tylko dlatego, żeby ludzie wiedzieli, użycie MediaTypeHeaderValue zwróci błąd, jeśli spróbujesz ustawić zestaw znaków, podobnie; response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
— MBak

3
Komentarz Johns'a do rozwiązania Carlo powiedział: Response.Content.Headers, ale używasz req.Content.Headers? tj. żądanie kontra odpowiedź?
— joedotnot

52

Jeśli nie przeszkadza ci mała zależność od biblioteki, Flurl.Http [ujawnienie: jestem autorem] sprawia, że ​​jest to bardzo proste. Ta PostJsonAsyncmetoda zajmuje się serializowaniem treści i ustawieniem content-typenagłówka oraz ReceiveJsondeserializacją odpowiedzi. Jeśli acceptnagłówek jest wymagany, musisz ustawić go sam, ale Flurl zapewnia całkiem czysty sposób na zrobienie tego:

using Flurl.Http;

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

Flurl używa HttpClient i Json.NET pod maską, i jest to PCL, więc będzie działał na różnych platformach.

PM> Install-Package Flurl.Http

Jak wysłać, jeśli treść jest podana w formularzu application / x-www-form-urlencoded?
— Vlado Pandžić

2
Użyj tego. Osiągnąłem w <1 minutę, co zajęło mi dużo czasu, żeby to rozgryźć. Dzięki za udostępnienie tej biblioteki za darmo.
— Najeeb,

35

spróbuj użyć TryAddWithoutValidation

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

4
nie działa daje mi niewłaściwie użytą nazwę nagłówka. Upewnij się, że nagłówki żądań są używane z HttpRequestMessage, nagłówki odpowiedzi z HttpResponseMessage oraz nagłówki treści z obiektami HttpContent. ”
— Martin Lietz

3
Ci z was, którzy zgłaszają „pracujący” lub „niedziałający”, HttpClient jest obecnie bardzo niejednoznacznym obiektem. Proszę podać pełną nazwę (spację) i zestaw .dll, z którego pochodzi.
— granadaCoder,

Misused header namebłąd zostanie potwierdzone z rdzeniem dotnet 2.2. Musiałem użyć odpowiedzi @ carlosfigueira stackoverflow.com/a/10679340/2084315 .
— ps2goat

działa dla pełnej .net działa (4.7).
— ZakiMa,

28

.Net próbuje zmusić cię do przestrzegania pewnych standardów, a mianowicie, że Content-Typenagłówek może być określony tylko na wnioski, które mają zawartość (np POST, PUTitp). Dlatego, jak wskazali inni, preferowanym sposobem ustawienia Content-Typenagłówka jest HttpContent.Headers.ContentTypewłaściwość.

Mając to na uwadze , niektóre interfejsy API (takie jak LiquidFiles Api , od 19.12.2016) wymagają ustawienia Content-Typenagłówka GETżądania. .Net nie zezwoli na ustawienie tego nagłówka na samym żądaniu - nawet przy użyciu TryAddWithoutValidation. Ponadto nie można określić Contentżądania dla - nawet jeśli ma ono zerową długość. Jedynym sposobem, w jaki mogłem to obejść, było uciekanie się do refleksji. Kod (na wypadek, gdyby ktoś go potrzebował) to

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

Edytować:

Jak zauważono w komentarzach, pole to ma różne nazwy w różnych wersjach biblioteki dll. W kodzie źródłowym na GitHub pole ma obecnie nazwę s_invalidHeaders. Przykład został zmodyfikowany, aby uwzględnić to zgodnie z sugestią @David Thompson.


1
To pole jest teraz s_invalidHeaders, więc użycie następujących zapewnia zgodność: 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
Dziekuję Dziekuję Dziękuję! Czasami mój mount zawiesza się i pojawia się ślinka, gdy uderzam w błąd Microsoft API. Byłem w stanie go wyczyścić po zobaczeniu twojego bardzo prostego posta. Nie zbyt złe ..
— Gerard ONeill

1
Nie jestem pewien, w jaki sposób ten kod spowodowałby katastrofalne błędy, które opisujesz. Czy możesz podać więcej szczegółów na temat swojego zastosowania i otrzymywanych błędów?
— erdomke

2
Łał. Tym bardziej, że metody GET Asp.net WebApi wymagają jawnego określenia Content-Type = (
— AlfeG

2
Holly Molly, nie mogę uwierzyć, że muszę się do tego uciekać. Od kiedy deweloperzy platformy .NET muszą trzymać rękę w tym, co mogę dodać do nagłówka HTTP? Wstrętny.
— mmix,

17

Niektóre dodatkowe informacje o .NET Core (po przeczytaniu postu erdomke o ustawianiu prywatnego pola do dostarczenia typu zawartości na żądanie, które nie zawiera treści) ...

Po debugowaniu mojego kodu nie widzę pola prywatnego, które można ustawić poprzez odbicie - pomyślałem więc, że spróbuję odtworzyć problem.

Próbowałem następujący kod przy użyciu .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;

I zgodnie z oczekiwaniami otrzymuję zbiorczy wyjątek z treścią "Cannot send a content-body with this verb-type."

Jeśli jednak zrobię to samo z .NET Core (1.1) - nie otrzymam wyjątku.Moja aplikacja na serwer dość szczęśliwie odpowiedziała i pobrano typ zawartości.

Byłem mile zaskoczony tym i mam nadzieję, że to komuś pomoże!


1
Dzięki, Jay - nie korzystam z rdzenia i użyję odpowiedzi erdomke. Doceniam fakt, że wypróbowano wszystkie rozsądne możliwości :).
— Gerard ONeill

1
nie działa .net 4 ({"Nie można wysłać treści z tym typem czasownika."})
— Tarek El-Mallah

3
@ TarekEl-Mallah Tak - przeczytaj komentarze w mojej odpowiedzi. Celem mojego postu było zilustrowanie, że nie działa w .NET 4, ale działa w rdzeniu .NET (to nie to samo). Będziesz musiał zobaczyć odpowiedź erdomeke na pytanie PO, aby móc zhakować ją do pracy w .NET 4.
— Jay

16

Zadzwoń AddWithoutValidationzamiast Add(patrz ten link MSDN ).

Alternatywnie, zgaduję, że interfejs API, którego używasz, naprawdę wymaga tylko tego dla żądań POST lub PUT (nie zwykłe żądania GET). W takim przypadku, gdy wywołujesz HttpClient.PostAsynci przekazujesz HttpContent, ustaw to na Headerswłaściwość tego HttpContentobiektu.


nie działa daje mi niewłaściwie użytą nazwę nagłówka. Upewnij się, że nagłówki żądań są używane z HttpRequestMessage, nagłówki odpowiedzi z HttpResponseMessage oraz nagłówki treści z obiektami HttpContent. ”
— Martin Lietz

3
AddWithoutValidation nie istnieje
— KansaiRobot

14

Dla tych, którzy się martwili charset

Miałem bardzo szczególny przypadek, że dostawca usług nie zaakceptował zestawu znaków, a oni odmówili zmiany podstruktury, aby na to pozwolić ... Niestety HttpClient ustawiał nagłówek automatycznie za pośrednictwem StringContent i bez względu na to, czy podasz null lub Encoding.UTF8, zawsze ustawi zestaw znaków ...

Dzisiaj byłem na krawędzi, aby zmienić podsystem; przechodząc od HttpClient do czegokolwiek innego, że coś przyszło mi do głowy ... dlaczego nie użyć refleksji, aby opróżnić „zestaw znaków”? ... I zanim jeszcze spróbowałem, pomyślałem o pewnym „może mogę to zmienić po inicjalizacji” i to zadziałało.

Oto jak ustawić dokładny nagłówek „application / json” bez „; 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;

Uwaga: Poniższa nullwartość nie będzie działać i dopisuje „; charset = utf-8”

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

EDYTOWAĆ

@DesertFoxAZ sugeruje, że można również użyć następującego kodu i działa dobrze. (sam tego nie testowałem)

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

1
stringContent.Headers.ContentType = new MediaTypeHeaderValue („application / json”); działa również
— DesertFoxAZ

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

To wszystko, czego potrzebujesz.

Korzystając z Newtonsoft.Json, jeśli potrzebujesz zawartości jako ciągu 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
Czy możesz podać małe wyjaśnienie tego, co robi?
— Alejandro

2
Pierwszy wiersz kończy się niepowodzeniem w CS0144: „Nie można utworzyć wystąpienia klasy abstrakcyjnej lub interfejsu„ HttpContent ””
— Randall Flagg

1
a następnieHttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
— art24war

2

Ok, to nie jest HTTPClient, ale jeśli możesz go użyć, WebClient jest dość łatwy:

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

Możesz tego użyć, to będzie praca!

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

Uważam to za najprostsze i najłatwiejsze do zrozumienia w następujący sposób:

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

Musisz to zrobić w następujący sposób:

    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;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.