Bagaimana Anda mengatur header Tipe Konten untuk permintaan HttpClient?

739

Saya mencoba mengatur Content-Typeheader suatu HttpClientobjek seperti yang dipersyaratkan oleh API yang saya panggil.

Saya mencoba mengatur Content-Typeseperti di bawah ini:

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

Ini memungkinkan saya untuk menambahkan Acceptheader tetapi ketika saya mencoba menambahkannya Content-Typemelempar pengecualian berikut:

Nama tajuk yang disalahgunakan. Pastikan header permintaan digunakan bersama HttpRequestMessage, header respons dengan HttpResponseMessage, dan header konten dengan HttpContentobjek.

Bagaimana saya bisa mengatur Content-Typeheader dalam HttpClientpermintaan?

mynameiscoffey
sumber
Anda dapat mengikuti bagaimana HttpWebRequest di .NET Core melakukannya (menggunakan HttpClient secara internal), lihat github.com/dotnet/corefx/blob/master/src/System.Net.Requests/… "SendRequest" metode
jiping-s

Jawaban:

928

Jenis konten adalah tajuk konten, bukan permintaan, itulah sebabnya mengapa ini gagal. AddWithoutValidationseperti yang disarankan oleh Robert Levy dapat berfungsi, tetapi Anda juga dapat mengatur tipe konten saat membuat konten permintaan itu sendiri (perhatikan bahwa cuplikan kode menambahkan "application / json" di dua tempat-untuk header Terima dan Tipe-Konten):

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
sumber
32
Atau, Response.Content.Headersakan bekerja sebagian besar waktu.
John Gietzen
4
@AshishJain Sebagian besar jawaban SO yang saya lihat melibatkan Response.Content.Headersuntuk ASP.Net Web API juga tidak berfungsi, tetapi Anda dapat dengan mudah mengaturnya menggunakan HttpContext.Current.Response.ContentTypejika Anda perlu.
jerhewet
6
@jewewet saya menggunakan cara berikut yang bekerja untuk saya. konten var = StringContent baru (data, Encoding.UTF8, "application / json");
Ashish-BeJovial
22
Content-Type adalah properti dari pesan HTTP dengan muatan; itu tidak ada hubungannya dengan permintaan vs tanggapan.
Julian Reschke
6
Menarik. Saya mencoba membuat StringContent baru dengan tiga parameter dan tidak berhasil. Saya kemudian secara manual: request.Content.Headers.Remove ("Content-Type") dan kemudian: request.Content.Headers.Add ("Content-Type", "application / query + json") dan berhasil. Aneh.
Bill Noel
163

Bagi mereka yang tidak melihat komentar Johns untuk solusi carlos ...

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
archgl
sumber
Itu membuat perbedaan mengunduh pdf. Dari telepon, ia mencoba mengunduh HTML. Setelah mengonversi ekstensi, file tersebut biasanya disandikan.
Matteo Defanti
Saya harus membuang .ToString () di akhir, tapi ya ini berhasil untuk implementasi layanan WCF.
John Meyer
2
Saya akhirnya akan mencari tahu apa jenis objek "req" adalah ... dengan coba-coba ........ TAPI akan bagus untuk menunjukkan itu. Terima kasih atas pertimbangan Anda.
granadaCoder
4
Asal tahu saja, menggunakan MediaTypeHeaderValue akan mengembalikan kesalahan jika mencoba mengatur charset, seperti itu; response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
MBak
3
Komentar Johns untuk solusi Carlo berkata Response.Content.Headers, tetapi Anda menggunakan req.Content.Headers? yaitu Permintaan vs Tanggapan?
joedotnot
52

Jika Anda tidak keberatan dengan ketergantungan perpustakaan kecil, Flurl.Http [pengungkapan: Saya pembuatnya] menjadikan ini uber-sederhana. Its PostJsonAsyncmetode membutuhkan perawatan dari kedua serialisasi konten dan pengaturan content-typeheader, dan ReceiveJsondeserializes respon. Jika accepttajuk diperlukan, Anda harus mengaturnya sendiri, tetapi Flurl juga menyediakan cara yang cukup bersih untuk melakukannya:

using Flurl.Http;

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

Flurl menggunakan HttpClient dan Json.NET di bawah tenda, dan ini adalah PCL sehingga akan bekerja pada berbagai platform.

PM> Install-Package Flurl.Http
Todd Menier
sumber
Bagaimana cara mengirim jika konten adalah aplikasi / x-www-form-urlencoded?
Vlado Pandžić
2
Menggunakannya Mencapai dalam <1 menit apa yang membuat saya butuh waktu lama untuk mencari tahu. Terima kasih telah menjaga perpustakaan ini gratis.
Najeeb
35

coba gunakan TryAddWithoutValidation

  var client = new HttpClient();
  client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
SharpCoder
sumber
4
tidak berfungsi memberi saya nama header 'Disalahgunakan. Pastikan header permintaan digunakan dengan HttpRequestMessage, header respons dengan HttpResponseMessage, dan header konten dengan objek HttpContent. '
Martin Lietz
3
Bagi Anda yang melaporkan "berfungsi" atau "tidak berfungsi", HttpClient adalah objek yang sangat ambigu akhir-akhir ini. Silakan laporkan nama lengkap (spasi) dan .dll perakitan dari mana asalnya.
granadaCoder
yang Misused header nameerror dikonfirmasi dengan dotnet inti 2.2. Saya harus menggunakan stackoverflow.com/a/10679340/2084315 jawaban @ carlosfigueira .
ps2goat
ini berfungsi untuk full .net works (4.7).
ZakiMa
28

.Net mencoba untuk memaksa Anda untuk mematuhi standar tertentu, yaitu bahwa Content-Typesundulan hanya dapat ditentukan pada permintaan yang memiliki konten (misalnya POST, PUT, dll). Oleh karena itu, seperti yang telah ditunjukkan orang lain, cara yang lebih disukai untuk mengatur Content-Typeheader adalah melalui HttpContent.Headers.ContentTypeproperti.

Dengan itu, API tertentu (seperti LiquidFiles Api , mulai 2016-12-19) mengharuskan pengaturan Content-Typeheader untuk GETpermintaan. .Net tidak akan mengizinkan pengaturan tajuk ini pada permintaan itu sendiri - bahkan menggunakan TryAddWithoutValidation. Selain itu, Anda tidak dapat menentukan a Contentuntuk permintaan - bahkan jika panjangnya nol. Satu-satunya cara saya bisa mengatasi ini adalah dengan menggunakan refleksi. Kode (jika diperlukan orang lain) adalah

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

Edit:

Seperti disebutkan dalam komentar, bidang ini memiliki nama yang berbeda dalam berbagai versi dll. Di kode sumber di GitHub , bidang ini saat ini bernama s_invalidHeaders. Contoh telah dimodifikasi untuk memperhitungkan ini sesuai saran dari @ David Thompson.

erdomke
sumber
1
Bidang ini sekarang s_invalidHeaders sehingga menggunakan yang berikut ini memastikan kompatibilitas: bidang var = typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.RindingFlags.Static). ?? typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
David Thompson
2
Terima kasih terima kasih terima kasih! Terkadang mount saya hang terbuka dan air liur keluar ketika saya gagal pada Microsoft API gagal .. Saya bisa membersihkannya setelah melihat posting Anda yang sangat mudah. Tidak terlalu buruk ..
Gerard ONeill
1
Saya bingung bagaimana kode ini akan menyebabkan kesalahan katastropik yang Anda gambarkan. Bisakah Anda memberikan detail lebih lanjut tentang kasus penggunaan Anda dan kesalahan yang Anda terima?
erdomke
2
Wow. Lebih hebatnya lagi, bahwa metode Asp.net WebApi GET membutuhkan Tipe-Konten yang akan ditentukan secara eksplisit = (
AlfeG
2
Holly molly, aku tidak percaya aku harus melakukan ini. Sejak kapan .NET framework devs perlu memegang tangan saya pada apa yang bisa saya tambahkan ke bagian header Http? Mengerikan.
mmix
17

Beberapa informasi tambahan tentang .NET Core (setelah membaca posting erdomke tentang pengaturan bidang pribadi untuk memasok jenis konten berdasarkan permintaan yang tidak memiliki konten) ...

Setelah men-debug kode saya, saya tidak dapat melihat bidang pribadi untuk diatur melalui refleksi - jadi saya pikir saya akan mencoba untuk membuat ulang masalah.

Saya telah mencoba kode berikut menggunakan .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;

Dan, seperti yang diharapkan, saya mendapatkan pengecualian agregat dengan konten "Cannot send a content-body with this verb-type."

Namun, jika saya melakukan hal yang sama dengan .NET Core (1.1) - Saya tidak mendapatkan pengecualian. Permintaan saya dengan senang hati dijawab oleh aplikasi server saya, dan jenis konten diambil.

Saya terkejut tentang hal itu, dan saya harap ini membantu seseorang!

Jay
sumber
1
Terima kasih, Jay - Tidak menggunakan inti, dan akan menggunakan jawaban erdomke. Saya menghargai mengetahui bahwa semua jalan masuk akal telah dicoba :).
Gerard ONeill
1
tidak berfungsi .net 4 ({"Tidak bisa mengirim konten-isi dengan tipe kata kerja ini."})
Tarek El-Mallah
3
@ TarekEl-Mallah Ya - harap baca komentar dalam jawaban saya. Inti dari posting saya adalah untuk menggambarkan bahwa itu tidak bekerja di .NET 4, tetapi itu berfungsi di .NET core (mereka bukan hal yang sama). Anda harus melihat jawaban erdomeke atas pertanyaan OP untuk dapat meretasnya agar berfungsi di .NET 4.
Jay
16

Panggil AddWithoutValidationalih-alih Add(lihat tautan MSDN ini ).

Atau, saya menduga API yang Anda gunakan benar-benar hanya memerlukan ini untuk permintaan POST atau PUT (bukan permintaan GET biasa). Dalam hal itu, ketika Anda menelepon HttpClient.PostAsyncdan meneruskan HttpContent, atur ini pada Headersproperti HttpContentobjek itu.

Robert Levy
sumber
tidak berfungsi memberi saya nama header 'Disalahgunakan. Pastikan header permintaan digunakan dengan HttpRequestMessage, header respons dengan HttpResponseMessage, dan header konten dengan objek HttpContent. '
Martin Lietz
3
AddWithoutValidation tidak ada
KansaiRobot
14

Bagi mereka yang bermasalah charset

Saya punya kasus yang sangat khusus bahwa penyedia layanan tidak menerima charset, dan mereka menolak untuk mengubah substruktur untuk mengizinkannya ... Sayangnya HttpClient sedang mengatur header secara otomatis melalui StringContent, dan tidak masalah jika Anda memasukkan null atau Encoding.UTF8, itu akan selalu mengatur charset ...

Hari ini saya berada di tepi untuk mengubah sub-sistem; pindah dari HttpClient ke hal lain, bahwa sesuatu muncul di benak saya ..., mengapa tidak menggunakan refleksi untuk mengosongkan "charset"? ... Dan bahkan sebelum saya mencobanya, saya memikirkan cara, "mungkin saya bisa mengubahnya setelah inisialisasi", dan itu berhasil.

Inilah cara Anda dapat mengatur header "application / json" tanpa "; 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;

Catatan: The nullnilai berikut tidak akan bekerja, dan append "; charset = utf-8"

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

EDIT

@DesertFoxAZ menyarankan bahwa kode berikut juga dapat digunakan dan berfungsi dengan baik. (tidak mengujinya sendiri)

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
deadManN
sumber
1
stringContent.Headers.ContentType = new MediaTypeHeaderValue ("application / json"); juga berfungsi
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"));

Itu semua yang Anda butuhkan.

Dengan menggunakan Newtonsoft.Json, jika Anda memerlukan konten sebagai string 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
sumber
1
Bisakah Anda memberikan sedikit penjelasan tentang apa fungsinya?
Alejandro
2
Baris pertama gagal dengan CS0144: "Tidak dapat membuat turunan dari kelas abstrak atau antarmuka 'HttpContent'"
Randall Flagg
1
dan kemudianHttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
art24war
2

Oke, ini bukan HTTPClient tetapi jika Anda dapat menggunakannya, WebClient cukup mudah:

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
sumber
1

Anda dapat menggunakan ini akan berhasil!

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
sumber
0

Saya merasa paling sederhana dan mudah dipahami dengan cara berikut:

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();
Goal Anshuman
sumber
0

Anda perlu melakukannya seperti ini:

    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;
pengguna890332
sumber