如何设置HttpClient请求的Content-Type标头?


738

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

我尝试设置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-TypeHttpClient请求中设置标题?


你可以按照如何HttpWebRequest的.NET中的核心做它(它使用HttpClient的内部),请参阅github.com/dotnet/corefx/blob/master/src/System.Net.Requests/... “sendRequest将”方法
周吉平-S

Answers:


928

内容类型是内容的标头,而不是请求的标头,这就是失败的原因。AddWithoutValidation如Robert Levy所建议的那样可能有效,但您也可以在创建请求内容本身时设置内容类型(请注意,代码段在两个位置为“ Accept”和“ Content-Type”标头添加了“ application / json”):

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 2012年

4
@AshishJain我见过的涉及Response.Content.HeadersASP.Net Web API的大多数SO回答都没有用,但是您可以HttpContext.Current.Response.ContentType根据需要轻松地设置它。
jerhewet 2014年

6
@jerhewet我以下列方式为我工作。var content = new StringContent(data,Encoding.UTF8,“ application / json”);
Ashish-BeJovial 2014年

22
Content-Type是带有有效负载的HTTP消息的属性;它与请求与响应无关。
朱利安·雷施克

6
有趣。我尝试用三个参数创建一个新的StringContent,但是它没有用。然后,我手动进行以下操作:request.Content.Headers.Remove(“ Content-Type”),然后:request.Content.Headers.Add(“ Content-Type”,“ application / query + json”)即可。奇。
比尔·诺埃尔

163

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

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

下载pdf有所不同。它试图通过电话下载HTML。转换扩展名后,文件通常已编码。
Matteo Defanti

最后,我不得不抛出.ToString(),但是可以,这适用于WCF服务实现。
约翰·迈耶

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吗?即请求与响应?
joedotnot

52

如果您不介意小的库依赖关系,可以使用Flurl.Http [披露:我是作者]来使这个超级简单。它的PostJsonAsync方法既要照顾内容的序列化又要设置content-type标题,并ReceiveJson反序列化响应。如果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

如果内容是application / x-www-form-urlencoded,如何发送?
VladoPandžić17年

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 core 2.2确认。我不得不使用@carlosfigueira的答案stackoverflow.com/a/10679340/2084315
ps2goat

它适用于完整的.net工程(4.7)。
ZakiMa

28

.NET试图迫使你遵守一定的标准,即在Content-Type头只能在有内容(例如,请求指定POSTPUT等等)。因此,正如其他人指出的那样,设置Content-Type标头的首选方法是通过HttpContent.Headers.ContentType属性。

话虽如此,某些API(例如,截至2016-12-19 的LiquidFiles Api)需要设置请求的Content-Type标头GET。.Net不允许在请求本身上设置此标头-甚至使用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的不同版本中具有不同的名称。在GitHub上源代码中,该字段当前名为s_invalidHeaders。根据@David Thompson的建议,对示例进行了修改以解决此问题。


1
此字段现在是s_invalidHeaders,因此使用以下命令可确保兼容性: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);
大卫·汤普森

2
谢谢你,谢谢你,谢谢你!有时,当我碰到Microsoft API失败时,我的安装架会挂起并流口水。.在看到您非常简单的文章后,我能够对其进行清理。不是糟糕..
杰拉德·奥尼尔

1
我对这段代码将如何导致您描述的灾难性错误感到困惑。您能否提供有关用例和收到的错误的更多详细信息?
erdomke '17

2
哇。更令人惊讶的是,Asp.net WebApi GET方法要求明确指定Content-Type =(
AlfeG

2
霍莉·莫莉,我不敢相信我必须诉诸于此。从什么时候开始.NET框架开发人员就需要握住我可以添加到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
谢谢,杰伊-不使用核心,将使用erdomke的答案。感谢您尝试了所有合理的方法:)。
Gerard ONeill

1
NET 4无法正常工作({“无法发送具有此动词类型的内容主体。”})
Tarek El-Mallah

3
@ TarekEl-Mallah是的-请阅读我的回答中的评论。我的帖子的全部目的是说明它在.NET 4中不起作用,但在.NET核心中却起作用(它们是不一样的)。你将不得不看到erdomeke的答案OP的问题能够把它破解工作中的.NET 4
周杰伦

16

致电AddWithoutValidation代替Add(请参阅此MSDN链接)。

另外,我猜您正在使用的API实际上仅需要POST或PUT请求(而不是普通的GET请求)。在这种情况下,当您调用HttpClient.PostAsync并传入时,请在该对象HttpContentHeaders属性上进行设置HttpContent


不起作用给了我一个'错误的标题名称。确保请求标头与HttpRequestMessage一起使用,响应标头与HttpResponseMessage一起使用,内容标头与HttpContent对象一起使用。
Martin Lietz

3
AddWithoutValidation不存在
KansaiRobot

14

对于那些困扰 charset

我有一个非常特殊的情况,服务提供者不接受字符集,并且他们拒绝更改子结构以允许它...不幸的是,HttpClient通过StringContent自动设置了标头,无论您传递null还是Encoding.UTF8,它将始终设置字符集...

今天,我处于改变子系统的边缘。从HttpClient迁移到其他事物,我想到了什么...,为什么不使用反射清空“字符集”?...在尝试之前,我想到了一种方法,“也许我可以在初始化后进行更改”,并且这种方法行得通。

这是您可以在没有“; charset = utf-8”的情况下设置确切的“ application / json”标头的方法。

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
您能对其做些小的解释吗?
亚历杭德罗

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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.