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

Posted

技术标签:

【中文标题】如何为 HttpClient 请求设置 Content-Type 标头?【英文标题】:How do you set the Content-Type header for an HttpClient request? 【发布时间】:2012-05-27 14:19:59 【问题描述】:

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

我尝试如下设置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 对象的内容标题。

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

【问题讨论】:

您可以按照 .NET Core 中的 HttpWebRequest 的方式进行操作(它在内部使用 HttpClient),请参阅github.com/dotnet/corefx/blob/master/src/System.Net.Requests/… "SendRequest" 方法 【参考方案1】:

内容类型是内容的标头,而不是请求的标头,这就是失败的原因。 AddWithoutValidation as suggested by Robert Levy 可能有效,但您也可以在创建请求内容本身时设置内容类型(注意代码 sn-p 在两个地方添加了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);
      );

【讨论】:

或者,Response.Content.Headers 大部分时间都可以工作。 @AshishJain 我看到的大多数 SO 答案都涉及到 ASP.Net Web API 的 Response.Content.Headers . @jerhewet 我以以下方式使用了对我有用的方法。 var content = new StringContent(data, Encoding.UTF8, "application/json"); Content-Type 是带有负载的 HTTP 消息的属性;它与请求与响应无关。 有趣。我尝试使用三个参数创建一个新的 StringContent ,但它不起作用。然后我手动: request.Content.Headers.Remove("Content-Type") 然后: request.Content.Headers.Add("Content-Type", "application/query+json") 并且它起作用了。奇数。【参考方案2】:

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

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

【讨论】:

下载 pdf 文件有所不同。它试图从手机下载 html。转换扩展名后,文件通常被编码。 我不得不在最后抛出 .ToString() ,但是是的,这适用于 WCF 服务实现。 我最终会通过反复试验找出对象类型“req”是什么......但如果能证明这一点会很棒。感谢您的考虑。 大家都知道,如果尝试设置字符集,使用 MediaTypeHeaderValue 将返回错误,如下所示; response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8"); Johns 对 Carlo 的解决方案的评论说 Response.Content.Headers,但您使用的是 req.Content.Headers 吗?即请求与响应?【参考方案3】:

如果您不介意小型库依赖项,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,如何发送? 用过。在不到 1 分钟的时间内完成了我花了很长时间才弄清楚的事情。感谢您免费提供此库。【参考方案4】:

尝试使用 TryAddWithoutValidation

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

【讨论】:

不工作给了我一个“误用的标题名称。确保请求标头与 HttpRequestMessage 一起使用,响应标头与 HttpResponseMessage 一起使用,内容标头与 HttpContent 对象一起使用。' 你们这些报告“工作”或“不工作”的人,现在 HttpClient 是一个非常模糊的对象。请报告它来自的全名(空格)和 .dll 程序集。 Misused header name 错误已在 dotnet core 2.2 中得到确认。我不得不使用@carlosfigueira 的答案***.com/a/10679340/2084315。【参考方案5】:

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

话虽如此,某些 API(例如 LiquidFiles Api,截至 2016 年 12 月 19 日)需要为 GET 请求设置 Content-Type 标头。 .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");

编辑:

如 cmets 中所述,该字段在不同版本的 dll 中具有不同的名称。在source code on GitHub 中,该字段当前名为s_invalidHeaders。该示例已根据@David Thompson 的建议进行了修改以解决此问题。

【讨论】:

此字段现在是 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); 谢谢谢谢谢谢!有时,当我遇到 Microsoft API 失败时,我的安装挂起并流口水。在看到您非常直截了当的帖子后,我能够清理它。不是坏.. 我对这段代码如何导致您描述的灾难性错误感到困惑。您能否提供有关您的用例和您收到的错误的更多详细信息? 哇。更令人惊叹的是,Asp.net WebApi GET 方法需要明确指定 Content-Type =( Holly molly,我不敢相信我不得不求助于这个。从什么时候开始 .NET 框架开发人员需要牵手我可以添加到 Http 标头部分的内容?可恶。【参考方案6】:

给那些困扰charset的人

我有一个非常特殊的情况,服务提供商不接受字符集,他们拒绝更改子结构以允许它...... 不幸的是HttpClient是通过StringContent自动设置header的,不管你传null还是Encoding.UTF8,它总是会设置charset...

今天我处于改变子系统的边缘;从 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 建议也可以使用以下代码并且可以正常工作。 (我自己没有测试,如果它工作的速率和信用他在 cmets)

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

【讨论】:

stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");也有效【参考方案7】:

关于 .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) 做同样的事情 - 我没有遇到异常。 我的服务器应用程序非常高兴地回答了我的请求,并且内容类型被捡起来了。

对此我感到很惊喜,我希望它对某人有所帮助!

【讨论】:

谢谢,Jay -- 不使用核心,将使用 erdomke 的答案。我很高兴知道所有合理的途径都已尝试:)。 not working .net 4 ("Cannot send a content-body with this verb-type.") @TarekEl-Mallah 是的 - 请阅读我回答中的 cmets。我这篇文章的重点是说明它在 .NET 4 中不起作用,但在 .NET 核心中确实起作用(它们不是一回事)。您必须查看 erdomeke 对 OP 问题的回答才能破解它以在 .NET 4 中工作。【参考方案8】:

请致电AddWithoutValidation 而不是Add(请参阅this MSDN link)。

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

【讨论】:

不工作给了我一个“误用的标题名称。确保请求标头与 HttpRequestMessage 一起使用,响应标头与 HttpResponseMessage 一起使用,内容标头与 HttpContent 对象一起使用。' AddWithoutValidation 不存在【参考方案9】:
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();
    

【讨论】:

你能简单解释一下它的作用吗? 第一行以 CS0144 失败:“无法创建抽象类或接口 'HttpContent' 的实例” 然后HttpMessageHandler handler = new WebRequestHandler(); HttpResponseMessage result; using (var client = (new HttpClient(handler, true))) result = client.PostAsync(fullUri, content).Result; 【参考方案10】:

你可以使用它,它会工作!

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

【讨论】:

这仅适用于 .NET Core,不适用于 .NET Framework。【参考方案11】:

好吧,它不是 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(...);
 

【讨论】:

【参考方案12】:

你需要这样做:

    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;

【讨论】:

【参考方案13】:

尝试使用 HttpClientFactory

services.AddSingleton<WebRequestXXX>()
        .AddHttpClient<WebRequestXXX>("ClientX", config =>
        
           config.BaseAddress = new System.Uri("https://jsonplaceholder.typicode.com");
           config.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
           config.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
        );

=======================

public class WebRequestXXXX

    private readonly IHttpClientFactory _httpClientFactory;

    public WebRequestXXXX(IHttpClientFactory httpClientFactory)
    
        _httpClientFactory = httpClientFactory;
    

    public List<Posts> GetAllPosts()
    
        using (var _client = _httpClientFactory.CreateClient("ClientX"))
        
            var response = _client.GetAsync("/posts").Result;

            if (response.IsSuccessStatusCode)
            
                var itemString = response.Content.ReadAsStringAsync().Result;
                var itemJson = System.Text.Json.JsonSerializer.Deserialize<List<Posts>>(itemString, 
                    new System.Text.Json.JsonSerializerOptions 
                    
                        PropertyNameCaseInsensitive = true
                    );

                return itemJson;
            
            else
            
                return new List<Posts>();
            
        
    

【讨论】:

【参考方案14】:

似乎微软试图强迫开发人员遵循他们的标准,甚至没有提供任何选项或设置来做其他事情,这真的很遗憾,特别是考虑到这是一个客户端,我们受服务器端要求的支配,特别是考虑到 Microsoft 服务器端框架本身需要它!

所以基本上微软在连接到他们的服务器技术时试图强迫我们养成良好的习惯,这迫使我们养成不良习惯......

如果 Microsoft 的任何人正在阅读此内容,请修复它...

对于需要 Get 等内容类型标头的任何人来说,无论哪种方式,而在较旧的 .Net 版本中,可以在 https://***.com/a/41231353/640195 使用@erdomke 的答案,不幸的是,这在较新的 .Net 中不再适用核心版本。

以下代码已经过测试,可与 .Net core 3.1 一起使用,从 GitHub 上的源代码来看,它似乎也可以与较新的 .Net 版本一起使用。

private void FixContentTypeHeaders()

    var assembly = typeof(System.Net.Http.Headers.HttpRequestHeaders).Assembly;
    var assemblyTypes = assembly.GetTypes();

    var knownHeaderType = assemblyTypes.FirstOrDefault(n => n.Name == "KnownHeader");
    var headerTypeField = knownHeaderType?
                .GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
                .FirstOrDefault(n => n.Name.Contains("HeaderType"));
    if (headerTypeField is null) return;

    var headerTypeFieldType = headerTypeField.FieldType;            
    var newValue = Enum.Parse(headerTypeFieldType, "All");

    var knownHeadersType = assemblyTypes.FirstOrDefault(n => n.Name == "KnownHeaders");
    var contentTypeObj = knownHeadersType.GetFields().FirstOrDefault(n => n.Name == "ContentType").GetValue(null);

    if (contentTypeObj is null) return;

    headerTypeField.SetValue(contentTypeObj, newValue);

【讨论】:

为什么所有这些反思都是必要的?决定 Content-Type 的是内容对象,您可以在 GET 请求以及 POST、PUT 等上添加内容对象。如果您没有内容对象(正文),则 Content-Type 是一个不相关的标头。 @John 有一些框架,包括微软自己的框架,即使是 Get 也需要标头,即使没有内容,也不要问我为什么......以及为什么他们的客户端在他们的服务器时删除它期待它... 我猜微软团队之间有一场战斗,我们是中间的猴子...... 除非 Content-Length 标头出现问题,否则创建一个派生的HttpContent 对象不是更好,它允许您指定标头而不是进行所有这些反射吗?【参考方案15】:

我通过 RestSharp 得到了答案:

        private async Task<string> GetAccessTokenAsync()
        
            var client = new RestClient(_baseURL);

            var request = new RestRequest("auth/v1/login", Method.POST, DataFormat.Json);

            request.AddHeader("Content-Type", "application/json");
            request.AddHeader("x-api-key", _apiKey);
            request.AddHeader("Accept-Language", "br");
            request.AddHeader("x-client-tenant", "1");
        
            ...
        

它对我有用。

【讨论】:

【参考方案16】:

对于那些想要专门将 Content-Type 设置为 Json 的人,您可以使用扩展方法 PostAsJsonAsync。

using System.Net.Http.Json; //this is needed for PostAsJsonAsync to work
//....
HttpClient client = new HttpClient();
HttpResponseMessage response = await
    client.PostAsJsonAsync("http://example.com/" + "relativeAddress",
                new
                
                    name = "John Doe",
                    age = 33
                );
//Do what you need to do with your response

这里的优点是代码更简洁,你可以避免字符串化的 json。更多详情请访问:https://docs.microsoft.com/en-us/previous-versions/aspnet/hh944339(v=vs.118)

【讨论】:

我遇到的问题是 PostAsJsonAsync 没有设置 Content-type 标头!!【参考方案17】:

我觉得最简单易懂的方式如下:

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

【讨论】:

【参考方案18】:

我最终遇到了类似的问题。 所以我发现Software PostMan可以在点击左上角的“代码”按钮时生成代码。从中我们可以看到“幕后”发生了什么,并且 HTTP 调用是用多种代码语言生成的; curl 命令、C# RestShat、java、nodeJs、...

这对我帮助很大,我最终没有使用 .Net 基础 HttpClient,而是使用了 RestSharp nuget 包。

希望可以帮助别人!

【讨论】:

【参考方案19】:

stringContent.Headers.ContentType = new MediaTypeHeaderValue(contentType); 捕获

还有 ? 是的! ? ...这解决了 ATS REST API 的问题:SharedKey 现在可以工作了! ? ? ? https://github.com/dotnet/runtime/issues/17036#issuecomment-212046628

【讨论】:

请编辑您的问题,尝试解释问题和您的解决方案(不带 imojis)

以上是关于如何为 HttpClient 请求设置 Content-Type 标头?的主要内容,如果未能解决你的问题,请参考以下文章

如何为我的 HttpClient PostAsync 第二个参数设置 HttpContent?

如何为构造函数包含 HttpClient 和一些字符串的类设置一些 .NET Core 依赖注入?

Angular:如何扩展 HttpClient?

如何为 HttpClient 建立合法的 URL

我应该如何为 dnx451 和 dnxcore50 引用 HttpClient?

如何为 cassandra 设置读取请求超时