Làm cách nào để bạn đặt tiêu đề Kiểu Nội dung cho yêu cầu HttpClient?


739

Tôi đang cố gắng đặt Content-Typetiêu đề của một HttpClientđối tượng theo yêu cầu của API mà tôi đang gọi.

Tôi đã thử thiết lập Content-Typenhư dưới đây:

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

Nó cho phép tôi thêm Accepttiêu đề nhưng khi tôi cố gắng thêm Content-Typenó sẽ ném ngoại lệ sau:

Tên tiêu đề sử dụng sai. Đảm bảo các tiêu đề yêu cầu được sử dụng với HttpRequestMessage, các tiêu đề phản hồi HttpResponseMessagevà các tiêu đề nội dung với HttpContentcác đối tượng.

Làm cách nào để đặt Content-Typetiêu đề trong HttpClientyêu cầu?


Bạn có thể theo dõi cách httpWebRequest trong .NET Core thực hiện (nó sử dụng HTTPClient bên trong), xem github.com/dotnet/corefx/blob/master/src/System.Net.Requests/ "Phương thức" SendRequest "
jiping-s

Câu trả lời:


928

Loại nội dung là một tiêu đề của nội dung, không phải của yêu cầu, đó là lý do tại sao điều này là thất bại. AddWithoutValidationnhư được đề xuất bởi Robert Levy có thể hoạt động, nhưng bạn cũng có thể đặt loại nội dung khi tự tạo nội dung yêu cầu (lưu ý rằng đoạn mã thêm "application / json" ở hai vị trí - cho các tiêu đề Chấp nhận và Loại nội dung):

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
Ngoài ra, Response.Content.Headerssẽ làm việc hầu hết thời gian.
John Gietzen

4
@AshishJain Hầu hết các câu trả lời SO tôi từng thấy liên quan đến Response.Content.HeadersAPI Web của ASP.Net đều không hoạt động, nhưng bạn có thể dễ dàng đặt nó bằng cách sử dụng HttpContext.Current.Response.ContentTypenếu bạn cần.
jerhewet

6
@jerhewet tôi đã sử dụng theo cách sau đây làm việc cho tôi. var content = new StringContent (data, Encoding.UTF8, "application / json");
Ashish-BeJovial

22
Content-Type là một thuộc tính của thông điệp HTTP có tải trọng; nó không có gì để làm với yêu cầu vs phản hồi.
Julian Reschke

6
Hấp dẫn. Tôi đã thử tạo StringContent mới với ba tham số và nó không hoạt động. Sau đó, tôi thủ công: request.Content.Headers.Remove ("Content-Type") và sau đó: request.Content.Headers.Add ("Content-Type", "application / query + json") và nó đã hoạt động. Lạ
Bill Noel

163

Đối với những người không thấy Johns bình luận về giải pháp carlos ...

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

Nó làm cho sự khác biệt tải về một bản pdf. Từ điện thoại, nó đã cố tải xuống một HTML. Sau khi chuyển đổi phần mở rộng, tập tin được mã hóa bình thường.
Matteo Defanti

Tôi đã phải ném .ToString () vào cuối, nhưng vâng, điều này có hiệu quả đối với việc triển khai dịch vụ WCF.
John Meyer

2
Cuối cùng tôi sẽ tìm ra loại đối tượng "req" là gì ... bằng cách dùng thử và lỗi ........ NHƯNG thật tuyệt khi thể hiện điều đó. Cám ơn bạn đã xem xét.
granadaCoder

4
Chỉ để mọi người biết, sử dụng MediaTypeHeaderValue sẽ trả về lỗi nếu cố gắng đặt bộ ký tự, như vậy; response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");
MBak

3
Johns nhận xét về giải pháp của Carlo cho biết Feedback.Content.Headers, nhưng bạn đang sử dụng req.Content.Headers? tức là Yêu cầu vs Phản hồi?
joedotnot

52

Nếu bạn không bận tâm đến một sự phụ thuộc thư viện nhỏ, Flurl.Http [tiết lộ: Tôi là tác giả] làm cho điều này trở nên đơn giản. PostJsonAsyncPhương pháp của nó đảm nhiệm cả việc tuần tự hóa nội dung và thiết lập content-typetiêu đề, và giải tuần tự hóa ReceiveJsonphản hồi. Nếu accepttiêu đề là bắt buộc, bạn sẽ cần phải tự đặt nó, nhưng Flurl cũng cung cấp một cách khá rõ ràng để làm điều đó:

using Flurl.Http;

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

Flurl sử dụng HttpClient và Json.NET trong phần mềm và đó là PCL nên nó sẽ hoạt động trên nhiều nền tảng khác nhau.

PM> Install-Package Flurl.Http

Làm cách nào để gửi nếu nội dung là application / x-www-form-urlencoding?
Vlado Pandžić

2
Sử dung nó. Đạt được trong <1 phút, điều khiến tôi mất nhiều thời gian để tìm ra. Cảm ơn vì đã giữ thư viện này miễn phí.
Najeeb

35

thử sử dụng TryAddWithoutValidation

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

4
không hoạt động cho tôi một tên tiêu đề bị sử dụng sai. Đảm bảo các tiêu đề yêu cầu được sử dụng với HttpRequestMessage, các tiêu đề phản hồi với HttpResponseMessage và các tiêu đề nội dung với các đối tượng HttpContent. '
Martin Lietz

3
Những người bạn báo cáo "làm việc" hoặc "không hoạt động", httpClient là một đối tượng rất mơ hồ những ngày này. Vui lòng báo cáo tên đầy đủ (không gian) và lắp ráp. Nó đến từ.
granadaCoder

các Misused header namelỗi được xác nhận với DotNet lõi 2.2. Tôi đã phải sử dụng câu trả lời của @ carlosfigueira stackoverflow.com/a/10679340/2084315 .
ps2goat

nó hoạt động cho toàn bộ .net hoạt động (4.7).
ZakiMa

28

Net cố gắng để buộc bạn phải tuân theo các tiêu chuẩn nhất định, cụ thể là các Content-Typetiêu đề chỉ có thể được xác định trên yêu cầu có nội dung (ví dụ POST, PUTvv). Do đó, như những người khác đã chỉ ra, cách ưa thích để đặt Content-Typetiêu đề là thông qua thuộc HttpContent.Headers.ContentTypetính.

Như đã nói, một số API nhất định (như LiquidFiles Api , kể từ 2016-12-19) yêu cầu đặt Content-Typetiêu đề cho GETyêu cầu. .Net sẽ không cho phép đặt tiêu đề này cho chính yêu cầu - ngay cả khi sử dụng TryAddWithoutValidation. Hơn nữa, bạn không thể chỉ định một Contentyêu cầu - ngay cả khi nó có độ dài bằng không. Cách duy nhất tôi có thể có được xung quanh điều này là dùng đến sự phản chiếu. Mã (trong trường hợp một số người khác cần nó) là

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

Biên tập:

Như đã lưu ý trong các bình luận, trường này có các tên khác nhau trong các phiên bản khác nhau của dll. Trong mã nguồn trên GitHub , trường hiện được đặt tên s_invalidHeaders. Ví dụ đã được sửa đổi để giải thích cho điều này theo gợi ý của @David Thompson.


1
Trường này hiện là s_invalidHeaders, do đó, sử dụng các mục sau đây đảm bảo tính tương thích: var field = typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("không hợp lệ", System.Reflection.BindingFlags.NonPublic | System.Refl ?? typeof (System.Net.Http.Headers.HttpRequestHeaders) .GetField ("s_invalidHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
David Thompson

2
Cảm ơn bạn, cảm ơn bạn, cảm ơn bạn! Đôi khi thú cưỡi của tôi bị treo và chảy nước dãi khi tôi nhấn Microsoft API thất bại .. Tôi đã có thể dọn sạch nó sau khi thấy bài viết rất đơn giản của bạn. Không quá tệ ..
Gerard ONeill

1
Tôi bối rối về cách mã này sẽ gây ra các lỗi nghiêm trọng mà bạn mô tả. Bạn có thể cung cấp thêm chi tiết về trường hợp sử dụng của bạn và các lỗi bạn đang nhận được không?
erdomke

2
Ồ Thậm chí đáng kinh ngạc hơn, các phương thức GET của Web.net Asp.net yêu cầu Kiểu nội dung phải được chỉ định rõ ràng = (
AlfeG

2
Holly molly, tôi không thể tin rằng tôi phải dùng đến điều này. Kể từ khi .NET framework phát triển, bạn cần phải nắm trong tay những gì tôi có thể thêm vào phần tiêu đề http? Đáng ghét.
mmix

17

Một số thông tin bổ sung về .NET Core (sau khi đọc bài đăng của erdomke về việc đặt trường riêng để cung cấp loại nội dung theo yêu cầu không có nội dung) ...

Sau khi gỡ lỗi mã của tôi, tôi không thể thấy trường riêng được đặt thông qua sự phản chiếu - vì vậy tôi nghĩ tôi sẽ cố gắng tạo lại vấn đề.

Tôi đã thử đoạn mã sau bằng .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;

Và, như mong đợi, tôi nhận được một ngoại lệ tổng hợp với nội dung "Cannot send a content-body with this verb-type."

Tuy nhiên, nếu tôi làm điều tương tự với .NET Core (1.1) - tôi không có ngoại lệ. Yêu cầu của tôi đã được ứng dụng máy chủ của tôi trả lời khá vui vẻ và loại nội dung đã được chọn.

Tôi đã rất ngạc nhiên về điều đó, và tôi hy vọng nó sẽ giúp được ai đó!


1
Cảm ơn, Jay - Không sử dụng cốt lõi, và sẽ sử dụng câu trả lời của erdomke. Tôi đánh giá cao khi biết rằng tất cả các con đường hợp lý đã được thử :).
Gerard ONeill

1
không hoạt động .net 4 ({"Không thể gửi nội dung với loại động từ này."})
Tarek El-Mallah

3
@ TarekEl-Mallah Có - vui lòng đọc các bình luận trong câu trả lời của tôi. Toàn bộ quan điểm của bài viết của tôi là để minh họa rằng nó không hoạt động trong .NET 4, nhưng nó hoạt động trong lõi .NET (chúng không giống nhau). Bạn sẽ phải xem câu trả lời của erdomeke cho câu hỏi của OP để có thể hack nó để hoạt động trong .NET 4.
Jay

16

Gọi AddWithoutValidationthay vì Add(xem liên kết MSDN này ).

Ngoài ra, tôi đoán API bạn đang sử dụng thực sự chỉ yêu cầu điều này cho các yêu cầu POST hoặc PUT (không phải các yêu cầu GET thông thường). Trong trường hợp đó, khi bạn gọi HttpClient.PostAsyncvà chuyển qua một HttpContent, hãy đặt cái này vào thuộc Headerstính của HttpContentđối tượng đó .


không hoạt động cho tôi một tên tiêu đề bị sử dụng sai. Đảm bảo các tiêu đề yêu cầu được sử dụng với HttpRequestMessage, các tiêu đề phản hồi với HttpResponseMessage và các tiêu đề nội dung với các đối tượng HttpContent. '
Martin Lietz

3
AddWithoutValidation không tồn tại
KansaiRobot

14

Dành cho những ai gặp rắc rối với charset

Tôi đã có một trường hợp rất đặc biệt là nhà cung cấp dịch vụ không chấp nhận bộ ký tự và họ từ chối thay đổi cấu trúc con để cho phép nó ... Thật không may, httpClient đã tự động đặt tiêu đề thông qua StringContent, và bất kể bạn có chuyển null hay Encoding.UTF8 không nó sẽ luôn đặt bộ ký tự ...

Hôm nay tôi đã ở rìa để thay đổi hệ thống phụ; chuyển từ httpClient sang bất cứ thứ gì khác, rằng một cái gì đó xuất hiện trong tâm trí tôi ..., tại sao không sử dụng sự phản chiếu để làm trống "bộ ký tự"? ... Và trước khi tôi thử nó, tôi đã nghĩ ra một cách, "có lẽ tôi có thể thay đổi nó sau khi khởi tạo", và điều đó đã hiệu quả.

Đây là cách bạn có thể đặt tiêu đề "application / json" chính xác mà không cần "; 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;

Lưu ý: Các nullgiá trị trong sau đây sẽ không làm việc, và append "; charset = utf-8"

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

BIÊN TẬP

@DesertFoxAZ đề xuất rằng cũng có thể sử dụng đoạn mã sau và hoạt động tốt. (không tự kiểm tra)

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

1
stringContent.Headers.ContentType = new MediaTypeHeaderValue ("application / json"); cũng hoạt động
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"));

Đó là tất cả những gì bạn cần.

Với việc sử dụng Newtonsoft.Json, nếu bạn cần một nội dung dưới dạng chuỗi 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
Bạn có thể đưa ra một lời giải thích nhỏ về những gì nó làm?
Alejandro

2
Dòng đầu tiên thất bại với CS0144: "Không thể tạo một thể hiện của lớp trừu tượng hoặc giao diện 'HttpContent'"
Randall Flagg

1
và sau đóHttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) { result = client.PostAsync(fullUri, content).Result; }
art24war

2

Ok, nó không phải là HTTPClient nhưng nếu bạn có thể sử dụng nó, WebClient khá dễ dàng:

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

Bạn có thể sử dụng nó sẽ được làm việc!

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

Tôi thấy nó đơn giản và dễ hiểu nhất theo cách sau:

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

Bạn cần làm như thế này:

    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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.