关于c#:如何为HttpClient请求设置Content-Type标头?

How do you set the Content-Type header for an HttpClient request?

我正在尝试根据我调用的API的要求设置HttpClient对象的Content-Type头。

我试着把Content-Type设置如下:

1
2
3
4
5
6
7
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时,它抛出了以下异常:

Misused header name. Make sure request headers are used with
HttpRequestMessage, response headers with HttpResponseMessage, and
content headers with HttpContent objects.

如何在HttpClient请求中设置Content-Type头?


内容类型是内容的头,而不是请求的头,这就是失败的原因。robert levy建议的AddWithoutValidation可能有效,但您也可以在创建请求内容本身时设置内容类型(请注意,代码片段在两个位置为accept和content-type头添加了"application/json"):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
      });


对于那些没有看到约翰对卡洛斯解决方案的评论的人…

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


如果你不介意一个小的库依赖,flurl.http[公开:我是作者]使这个uber变得简单。它的PostJsonAsync方法负责对内容进行序列化和设置content-type头,ReceiveJson对响应进行反序列化。如果需要accept头文件,您需要自己设置,但是flurl也提供了一种非常干净的方法:

1
2
3
4
5
6
using Flurl.Http;

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

Flurl在引擎盖下使用httpclient和json.net,它是一个PCL,因此它可以在各种平台上工作。

1
PM> Install-Package Flurl.Http


尝试使用TryAddWithout验证

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


.NET试图强制您遵守某些标准,即只有在具有内容的请求(例如,POSTPUT等)上才能指定content-type头。因此,如其他人所指出的,设置content-type头的首选方法是通过HttpContent.Headers.ContentType属性。

有鉴于此,某些API(如LiquidFiles API,截至2016-12-19)需要为GET请求设置content-type头。.NET不允许根据请求本身设置此头文件--即使使用TryAddWithoutValidation。此外,您不能为请求指定一个Content——即使它的长度为零。我唯一能避开这件事的方法就是反思。代码(以防其他人需要它)是

1
2
3
4
5
6
7
8
9
10
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中具有不同的名称。在Github上的源代码中,该字段当前命名为s_invalidHeaders。根据@david thompson的建议,对示例进行了修改,以说明这一点。


调用AddWithoutValidation,而不是Add(参见此msdn链接)。

或者,我猜您使用的API实际上只需要在POST或PUT请求(而不是普通的GET请求)中使用它。在这种情况下,当您调用HttpClient.PostAsync并传入一个HttpContent时,将其设置为该HttpContent对象的Headers属性。


有关.NET核心的一些额外信息(在阅读了Erdomke关于设置私有字段以在没有内容的请求上提供内容类型的文章之后)…

调试完代码后,我看不到要通过反射设置的私有字段,所以我想我会尝试重新创建这个问题。

我使用.NET 4.6尝试了以下代码:

1
2
3
4
5
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核心(1.1)做同样的事情,我不会得到异常。我的请求被我的服务器应用程序很高兴地回答了,内容类型被接收了。

我对此感到很惊喜,希望它能帮助别人!


1
2
3
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字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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();
    }
}


好吧,这不是httpclient,但是如果你能使用它,webclient就很容易了:

1
2
3
4
5
6
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(...);
 }