คุณจะตั้งค่าหัวข้อประเภทเนื้อหาสำหรับคำขอ HttpClient ได้อย่างไร


739

ฉันกำลังพยายามตั้งContent-Typeส่วนหัวของHttpClientวัตถุตาม API ที่ฉันกำลังเรียก

ฉันลองตั้งค่าContent-Typeดังนี้:

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

มันช่วยให้ฉันเพิ่มAcceptส่วนหัว แต่เมื่อฉันพยายามเพิ่มContent-Typeมันจะโยนข้อยกเว้นต่อไปนี้:

ชื่อส่วนหัวที่ใช้ผิด ตรวจสอบให้แน่ใจว่ามีการใช้ส่วนหัวคำขอพร้อม HttpRequestMessageส่วนหัวตอบกลับHttpResponseMessageและส่วนหัวเนื้อหากับHttpContentวัตถุ

ฉันจะตั้งค่าContent-Typeส่วนหัวในHttpClientคำขอได้อย่างไร


คุณสามารถทำตามวิธี HttpWebRequest ใน .NET หลักไม่ได้ (มันใช้ HttpClient ภายใน) ดูgithub.com/dotnet/corefx/blob/master/src/System.Net.Requests/... "SendRequest" วิธีการ
Jiping-S

คำตอบ:


928

ประเภทเนื้อหาเป็นส่วนหัวของเนื้อหาไม่ใช่ของคำขอซึ่งเป็นสาเหตุที่ทำให้เกิดความล้มเหลว AddWithoutValidationตามที่แนะนำโดย Robert Levy อาจใช้งานได้ แต่คุณสามารถตั้งค่าประเภทเนื้อหาเมื่อสร้างเนื้อหาคำขอด้วยตนเอง (โปรดทราบว่าข้อมูลโค้ดเพิ่ม "application / json" ในสองตำแหน่งสำหรับส่วนหัว Accept และ 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
หรือResponse.Content.Headersจะทำงานเกือบตลอดเวลา
John Gietzen

4
@AshishJain คำตอบส่วนใหญ่ที่ฉันเคยเห็นเกี่ยวกับResponse.Content.HeadersASP.Net Web API ก็ไม่ได้ทำงานเช่นกัน แต่คุณสามารถตั้งค่าได้โดยง่ายHttpContext.Current.Response.ContentTypeหากคุณต้องการ
jerhewet

6
@ jerhewet ฉันใช้ในวิธีต่อไปซึ่งทำงานได้สำหรับฉัน var content = ใหม่ StringContent (ข้อมูล, Encoding.UTF8, "application / json");
Ashish-BeJovial

22
Content-Type เป็นคุณสมบัติของข้อความ HTTP พร้อมเพย์โหลด มันไม่เกี่ยวอะไรกับคำขอเทียบกับการตอบสนอง
Julian Reschke

6
น่าสนใจ ฉันพยายามสร้าง StringContent ใหม่โดยใช้พารามิเตอร์สามตัว แต่ก็ไม่ได้ผล ฉันด้วยตนเอง: request.Content.Headers.Remove ("Content-Type") และจากนั้น: request.Content.Headers.Add ("Content-Type", "application / query + json") และใช้งานได้ แปลก
Bill Noel

163

สำหรับผู้ที่ไม่เห็นความเห็นของ Johns ถึงวิธีการแก้ปัญหา carlos ...

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

มันสร้างความแตกต่างในการดาวน์โหลดไฟล์ PDF จากโทรศัพท์พยายามดาวน์โหลด HTML หลังจากแปลงส่วนขยายแล้วไฟล์จะถูกเข้ารหัสตามปกติ
Matteo Defanti

ฉันต้องโยน. ToString () ในตอนท้าย แต่ใช่แล้วนี่ใช้งานได้กับบริการ WCF
John Meyer

2
ในที่สุดฉันก็จะคิดออกว่าวัตถุประเภท "req" คือ ... โดยการทดลองและข้อผิดพลาด ........ แต่มันจะดีที่จะแสดงให้เห็นว่า ขอขอบคุณสำหรับการพิจารณาของคุณ.
granadaCoder

4
ผู้คนรู้ดีว่าการใช้ MediaTypeHeaderValue จะส่งคืนข้อผิดพลาดหากพยายามตั้งค่าชุดอักขระเช่นนั้น response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
MBak

3
Johns แสดงความคิดเห็นต่อโซลูชันของ Carlo ที่ Response.Content.Headers แต่คุณกำลังใช้ req.Content.Headers? เช่นขอ vs การตอบสนอง?
joedotnot

52

หากคุณไม่สนใจการพึ่งพาห้องสมุดขนาดเล็กFlurl.Http [เปิดเผย: ฉันเป็นผู้แต่ง] ทำให้สิ่งนี้ง่ายมาก ใช้PostJsonAsyncวิธีการดูแลของทั้งสอง serializing เนื้อหาและการตั้งค่าcontent-typeส่วนหัวและReceiveJsondeserializes การตอบสนอง หากacceptจำเป็นต้องใช้ส่วนหัวคุณจะต้องตั้งค่าด้วยตัวคุณเอง แต่ Flurl ก็มีวิธีที่สะอาดในการทำเช่นกัน:

using Flurl.Http;

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

Flurl ใช้ HttpClient และ Json.NET ภายใต้ประทุนและเป็น PCL ดังนั้นมันจะทำงานบนแพลตฟอร์มที่หลากหลาย

PM> Install-Package Flurl.Http

จะส่งข้อมูลอย่างไรถ้าแอปพลิเคชั่น / x-www-form-urlencoded
Vlado Pandžić

2
ใช้มัน ทำได้ใน <1 นาทีสิ่งที่ทำให้ฉันต้องใช้เวลานานในการคิดออก ขอขอบคุณที่ทำให้ห้องสมุดนี้เป็นอิสระ
Najeeb

35

ลองใช้ TryAddWithoutValidation

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

4
ไม่ทำงานให้ฉัน 'ชื่อส่วนหัวที่ใช้ผิด ตรวจสอบให้แน่ใจว่ามีการใช้ส่วนหัวคำขอกับ HttpRequestMessage ส่วนหัวการตอบกลับด้วย HttpResponseMessage และส่วนหัวเนื้อหาที่มีวัตถุ HttpContent '
Martin Lietz

3
พวกที่คุณรายงานว่า "ทำงาน" หรือ "ไม่ทำงาน" HttpClient เป็นวัตถุที่คลุมเครือมากในทุกวันนี้ โปรดรายงานชื่อเต็ม (ช่องว่าง) และชุด. dll ที่มาจาก
granadaCoder

Misused header nameข้อผิดพลาดได้รับการยืนยันกับ dotnet 2.2 หลัก ผมใช้ @ carlosfigueira คำตอบของstackoverflow.com/a/10679340/2084315
ps2goat

มันใช้งานได้กับ. net ทั้งหมด (4.7)
ZakiMa

28

สุทธิพยายามที่จะบังคับให้คุณปฏิบัติตามมาตรฐานบางอย่างคือว่าContent-Typeส่วนหัวเท่านั้นที่สามารถระบุไว้ในคำขอที่มีเนื้อหา (เช่นPOST, PUTฯลฯ ) ดังนั้นตามที่คนอื่น ๆ ระบุไว้วิธีที่แนะนำในการตั้งค่าContent-Typeส่วนหัวคือผ่านHttpContent.Headers.ContentTypeคุณสมบัติ

ด้วยที่กล่าวไว้ APIs บางอย่าง (เช่นLiquidFiles Apiณ วันที่ 2016-12-19) ต้องมีการตั้งค่าContent-Typeส่วนหัวสำหรับGETคำขอ สุทธิจะไม่อนุญาตให้ตั้งค่าส่วนหัวนี้ตามคำขอของตัวเอง - TryAddWithoutValidationแม้กระทั่งการใช้ นอกจากนี้คุณไม่สามารถระบุContentคำขอได้แม้ว่าจะมีความยาวเป็นศูนย์ก็ตาม วิธีเดียวที่ฉันจะได้รับรอบนี้ก็เพื่อสะท้อน รหัส (ในกรณีที่บางคนต้องการมัน) คือ

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

แก้ไข:

ดังที่ระบุไว้ในความคิดเห็นฟิลด์นี้มีชื่อแตกต่างกันใน dll รุ่นต่าง ๆ ในรหัสที่มาบน GitHubs_invalidHeadersฟิลด์เป็นชื่อปัจจุบัน ตัวอย่างได้รับการแก้ไขเพื่อบัญชีตามข้อเสนอแนะของ @David Thompson


1
ขณะนี้ฟิลด์นี้คือ s_invalidHeaders ดังนั้นการใช้สิ่งต่อไปนี้จะช่วยให้แน่ใจได้ถึงความเข้ากันได้: var field = typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags ?? typeof (System.Net.Http.Headers.HttpRequestHeaders). GetField ("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
David Thompson

2
ขอบคุณขอบคุณขอบคุณ! บางครั้งการเมาค้างของฉันเปิดค้างไว้และ drool ออกมาเมื่อฉันกดบน Microsoft API ล้มเหลว .. ฉันสามารถทำความสะอาดได้หลังจากเห็นโพสต์ที่ตรงไปตรงมา ไม่เลวร้ายเกินไป ..
เจอราร์ด ONeill

1
ฉันสับสนว่ารหัสนี้จะทำให้เกิดข้อผิดพลาดร้ายแรงที่คุณอธิบายได้อย่างไร คุณสามารถให้รายละเอียดเพิ่มเติมเกี่ยวกับกรณีการใช้งานและข้อผิดพลาดที่คุณได้รับ?
erdomke

2
ว้าว. ว้าวยิ่งไปกว่านั้นวิธีการ Asp.net WebApi GET ต้องการให้ระบุประเภทเนื้อหาอย่างชัดเจน = (
AlfeG

2
ฮอลลี่มอลลี่ฉันไม่อยากจะเชื่อเลยว่าฉันต้องหันหน้าไปทางนี้ ตั้งแต่. NET Framework devs จำเป็นต้องกุมมือของฉันในสิ่งที่ฉันสามารถเพิ่มในส่วนหัว Http ได้อย่างไร น่ารังเกียจ
mmix

17

ข้อมูลเพิ่มเติมบางอย่างเกี่ยวกับ. NET Core (หลังจากอ่านโพสต์ของ erdomke เกี่ยวกับการตั้งค่าฟิลด์ส่วนตัวเพื่อระบุประเภทเนื้อหาตามคำขอที่ไม่มีเนื้อหา) ...

หลังจากตรวจแก้จุดบกพร่องรหัสของฉันฉันไม่เห็นเขตข้อมูลส่วนตัวที่ตั้งผ่านการสะท้อนดังนั้นฉันคิดว่าฉันพยายามสร้างปัญหาขึ้นใหม่

ฉันได้ลองใช้รหัสต่อไปนี้โดยใช้. 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;

และตามที่คาดไว้ฉันได้รับข้อยกเว้นโดยรวมเกี่ยวกับเนื้อหา "Cannot send a content-body with this verb-type."

อย่างไรก็ตามถ้าฉันทำสิ่งเดียวกันกับ. NET Core (1.1) - ฉันไม่ได้รับการยกเว้น แอปพลิเคชันเซิร์ฟเวอร์ของฉันตอบรับคำขอของฉันอย่างมีความสุขและมีการหยิบเนื้อหาประเภทนั้น

ฉันประหลาดใจเกี่ยวกับเรื่องนี้และฉันหวังว่ามันจะช่วยให้ใครบางคน!


1
ขอบคุณ Jay - ไม่ได้ใช้หลักและจะใช้คำตอบของ erdomke ฉันซาบซึ้งที่ทราบว่าได้ลองทุกเส้นทางที่เหมาะสมแล้ว :)
เจอราร์ด ONeill

1
ไม่ทำงาน. net 4 ({"ไม่สามารถส่งเนื้อหาเนื้อหาด้วยคำกริยาประเภทนี้"})
Tarek El-Mallah

3
@ TarekEl-Mallah ใช่ - โปรดอ่านความคิดเห็นในคำตอบของฉัน จุดทั้งหมดของโพสต์ของฉันคือการแสดงให้เห็นว่ามันไม่ทำงานใน. NET 4 แต่มันจะทำงานใน. NET core (มันไม่เหมือนกัน) คุณจะต้องดูคำตอบของ erdomeke คำถาม OP ที่จะสามารถที่จะตัดมันไปทำงานใน .NET 4.
เจ

16

โทรAddWithoutValidationแทนAdd(ดูลิงค์ MSDN นี้ )

หรือฉันคาดเดา API ที่คุณใช้จริง ๆ ใช้เพียงแค่นี้สำหรับคำขอ POST หรือ PUT (ไม่ใช่คำขอ GET ทั่วไป) ในกรณีนั้นเมื่อคุณโทรHttpClient.PostAsyncและส่งต่อให้HttpContentตั้งค่านี้ในHeadersคุณสมบัติของHttpContentวัตถุนั้น


ไม่ทำงานให้ฉัน 'ชื่อส่วนหัวที่ใช้ผิด ตรวจสอบให้แน่ใจว่ามีการใช้ส่วนหัวคำขอกับ HttpRequestMessage ส่วนหัวการตอบกลับด้วย HttpResponseMessage และส่วนหัวเนื้อหาที่มีวัตถุ HttpContent '
Martin Lietz

3
AddWithoutValidation ไม่มีอยู่
KansaiRobot

14

สำหรับผู้ที่มีปัญหา charset

ฉันมีกรณีพิเศษมากที่ผู้ให้บริการไม่ยอมรับชุดอักขระและพวกเขาปฏิเสธที่จะเปลี่ยนโครงสร้างย่อยเพื่ออนุญาต ... ขออภัย HttpClient กำลังตั้งค่าส่วนหัวโดยอัตโนมัติผ่าน StringContent และไม่ว่าคุณจะผ่าน Null หรือ Encoding.UTF8 มันจะตั้งค่าชุดอักขระเสมอ ...

วันนี้ฉันอยู่ที่ขอบเพื่อเปลี่ยนระบบย่อย; ย้ายจาก HttpClient ไปเป็นอย่างอื่นนั่นเป็นสิ่งที่อยู่ในใจของฉัน ... ทำไมไม่ใช้การไตร่ตรองเพื่อล้าง "ชุดอักขระ"? ... และก่อนที่ฉันจะลองฉันคิดว่า "บางทีฉันอาจเปลี่ยนได้หลังจากการเริ่มต้น" และนั่นก็ใช้ได้

นี่คือวิธีที่คุณสามารถตั้งค่าส่วนหัว "application / json" โดยไม่ต้อง "; 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;

หมายเหตุ:nullค่าในต่อไปนี้จะไม่ทำงานและผนวก "; charset = UTF-8"

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

แก้ไข

@DesertFoxAZ แนะนำว่ายังสามารถใช้รหัสต่อไปนี้และทำงานได้ดี (ไม่ได้ทดสอบด้วยตนเอง)

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

1
stringContent.Headers.ContentType = new MediaTypeHeaderValue ("application / json"); ยังใช้ได้
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"));

มันคือทั้งหมดที่คุณต้องการ

ด้วยการใช้ Newtonsoft.Json หากคุณต้องการเนื้อหาเป็นสตริง 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
คุณช่วยอธิบายอะไรเล็กน้อยได้บ้าง
Alejandro

2
บรรทัดแรกล้มเหลวด้วย CS0144: "ไม่สามารถสร้างอินสแตนซ์ของคลาสนามธรรมหรืออินเตอร์เฟส 'HttpContent'"
Randall Flagg

1
และจากนั้นHttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
art24war

2

ตกลงมันไม่ใช่ HTTPClient แต่ถ้าคุณสามารถใช้ได้ WebClient นั้นค่อนข้างง่าย:

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

คุณสามารถใช้มันจะทำงาน!

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

ฉันคิดว่ามันง่ายและเข้าใจง่ายที่สุดในวิธีต่อไปนี้:

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

คุณต้องทำเช่นนี้:

    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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.