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?

mynameiscoffey
źródło
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/ ... MetodaSendRequest
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);
      });
carlosfigueira
źródło
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");
archgl
źródło
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
Todd Menier
źródło
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");
SharpCoder
źródło
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.

erdomke
źródło
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!

Sójka
źródło
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.

Robert Levy
źródło
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");
deadManN
źródło
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();
    }
}
art24war
źródło
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(...);
 }
Ziba Leah
źródło
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();
Kumaran
źródło
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();
Anshuman Goel
źródło
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;
użytkownik890332
źródło