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

myn*_*fey 677 c# asp.net api rest http

我正在尝试根据我调用的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");
    // ...
}
Run Code Online (Sandbox Code Playgroud)

它允许我添加Accept标题但是当我尝试添加Content-Type它时抛出以下异常:

未使用的标题名称.确保请求标头与对象HttpRequestMessage一起使用 ,响应标头HttpResponseMessage和带有HttpContent对象的内容标头.

如何Content-TypeHttpClient请求中设置标头?

car*_*ira 844

内容类型是内容的标题,而不是请求的标题,这就是失败的原因.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);
      });
Run Code Online (Sandbox Code Playgroud)

  • 或者,`Response.Content.Headers`大部分时间都可以工作. (29认同)
  • Content-Type是带有效负载的HTTP消息的属性; 它与请求与响应无关. (19认同)
  • 有趣.我尝试用三个参数创建一个新的StringContent,它不起作用.然后我手动:request.Content.Headers.Remove("Content-Type")然后:request.Content.Headers.Add("Content-Type","application/query + json")并且它有效.奇. (6认同)
  • @jerhewet我用以下方式为我工作.var content = new StringContent(data,Encoding.UTF8,"application/json"); (5认同)
  • @AshishJain我见过的大部分SO答案涉及到ASP.Net Web API的"Response.Content.Headers"也没有用,但你可以使用`HttpContext.Current.Response.ContentType`轻松设置它.需要. (4认同)
  • 什么是"relativeAddress"位? (4认同)
  • 应该处理StringContent (3认同)
  • HttpClient应该被重用(静态)并且这样使用(不要设置基址,默认请求头等) (2认同)
  • 这是不好的.你应该重用HttpClient.如果您将HttpClient用于许多不同的相关问题,则应该使用DefaultRequestHeaders.HttpClient应该与实例化为单例密切相关.阿卡重新使用. (2认同)
  • 你有什么`GET`方法,如何用`HttpClien.GetAsync()'设置`Content-Type` (2认同)
  • @ LP13,GET请求通常没有请求正文,因此您不需要Content-Type标头(因为没有内容)。 (2认同)
  • 需要明确的是,System.Net.Http 在这里提供了一个糟糕的实现。Content-Type _is_ 是一个 HTTP 请求标头和一个暗示的实现,否则会产生误导和混乱。但是,当您使用 dotnet 时,您必须忍受它。(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type)。 (2认同)

arc*_*hgl 150

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

req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Run Code Online (Sandbox Code Playgroud)

  • 只是让人们知道,如果尝试设置字符集,使用 MediaTypeHeaderValue 将返回错误,如下所示;```response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml; charset=utf-8");``` (5认同)
  • 我最终将通过反复试验弄清对象类型“ req”是什么……但是,证明这一点将是很棒的。谢谢您的考虑。 (2认同)
  • Johns在对Carlo的解决方案发表评论时说Response.Content.Headers,但是您正在使用req.Content.Headers吗?即请求与响应? (2认同)

Tod*_*ier 49

如果你不介意一个小的库依赖,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>();
Run Code Online (Sandbox Code Playgroud)

Flurl使用HttpClient和Json.NET,它是一个PCL,因此它可以在各种平台上运行.

PM> Install-Package Flurl.Http
Run Code Online (Sandbox Code Playgroud)

  • 如果内容是 application/x-www-form-urlencoded 如何发送? (2认同)
  • 用过的 在不到1分钟的时间内就实现了,这让我花了很长时间才弄清楚。感谢您免费使用此库。 (2认同)

Sha*_*der 28

尝试使用TryAddWithoutValidation

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

  • 不工作给了我一个'Misused标题名称.确保请求标头与HttpRequestMessage一起使用,响应标头与HttpResponseMessage一起使用,内容标头与HttpContent对象一起使用. (4认同)
  • 那些报告“工作”或“不工作”的人,现在 HttpClient 是一个非常模糊的对象。请报告它来自的全名(空格)和 .dll 程序集。 (3认同)

erd*_*mke 25

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

话虽如此,某些API(例如LiquidFiles Api,截至2016-12-19)需要Content-TypeGET请求设置标头..Net不允许在请求本身上设置此标头 - 即使使用TryAddWithoutValidation.此外,您不能Content为请求指定a - 即使它是零长度.我似乎唯一可以解决这个问题的方法就是求助于反思.代码(如果其他人需要它)是

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");
Run Code Online (Sandbox Code Playgroud)

编辑:

如评论中所述,该字段在dll的不同版本中具有不同的名称.在GitHub源代码中,该字段当前已命名s_invalidHeaders.根据@David Thompson的建议,该示例已经过修改以解释此问题.

  • 谢谢你,谢谢你,谢谢你!有时,当我碰到Microsoft API失败时,我的挂架会打开并流口水。.在看到您非常简单的文章后,我能够对其进行清理。还不错 (2认同)
  • 哇。更令人惊讶的是,Asp.net WebApi GET方法要求明确指定Content-Type =( (2认同)
  • 霍莉·莫莉,我不敢相信我必须诉诸于此。从什么时候开始.NET框架开发人员需要牵手我可以添加到Http标头部分的内容?可恶。 (2认同)

Rol*_*and 21

诀窍是您可以设置各种标头,例如:

HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("Accept-Language", "en"); //works OK
Run Code Online (Sandbox Code Playgroud)

但没有任何标题。例如:

request.Headers.Add("Content-Type", "application/json");//wrong
Run Code Online (Sandbox Code Playgroud)

将引发运行时异常Misused header name。这似乎可行:

request.Headers.Add(
   HttpRequestHeader.ContentType.ToString(), //useless
   "application/json"
);
Run Code Online (Sandbox Code Playgroud)

但这给出了一个名为 的无用标头ContentType,没有连字符。标头名称不区分大小写,但对连字符非常敏感。

解决方案是在将body添加到http请求的Content部分时声明body的编码和类型:

string Body = "...";
request.Content = new StringContent(Body, Encoding.UTF8, "application/json");
Run Code Online (Sandbox Code Playgroud)

只有这样,适用的 http 标头才会自动添加到请求中,例如:

Content-Type: application/json; charset=utf-8
Run Code Online (Sandbox Code Playgroud)

在没有代理服务器的机器上使用 Fiddler 很难发现这一点。Visual Studio 曾经有一个网络工具,您可以在其中检查所有标头,但仅限于 2015 版本,而不是较新版本 2017 或 2022。如果您使用调试器检查 ,您将request.Headers找不到StringContent().

  • @Xserge当然可以,但最初的问题不是如何设置 ContentType 标头,而是如何设置 Content-Type 标头。如果某些 RFC 要求您设置某些标头,则标头名称不区分大小写,但肯定区分连字符。正如我在回答中所解释的。祝您节日快乐! (2认同)

Jay*_*Jay 16

关于.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;
Run Code Online (Sandbox Code Playgroud)

并且,正如预期的那样,我得到了内容的聚合异常 "Cannot send a content-body with this verb-type."

但是,如果我使用.NET Core(1.1)做同样的事情 - 我没有例外.我的服务器应用程序很满意地回答了我的请求,并且拾取了内容类型.

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

  • @ TarekEl-Mallah是的-请阅读我的回答中的评论。我的帖子的全部目的是说明它在.NET 4中不起作用,但在.NET核心中却起作用(它们是不一样的)。您将必须看到erdomeke对OP问题的回答,才能对其进行破解以在.NET 4中工作。 (3认同)

Rob*_*evy 15

调用AddWithoutValidation而不是Add(请参阅此MSDN链接).

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

  • AddWithoutValidation引发相同的错误 (3认同)
  • AddWithoutValidation不存在 (3认同)

小智 7

你可以用这个,它会起作用的!

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();
Run Code Online (Sandbox Code Playgroud)


dea*_*anN 6

对于那些困扰 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;
Run Code Online (Sandbox Code Playgroud)

注意:null后面的值不起作用,并附加“; charset = utf-8”

return new StringContent(jsonRequest, null, "application/json");
Run Code Online (Sandbox Code Playgroud)

  • stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 也有效 (2认同)

yoe*_*alb 5

对于任何需要 Get 等内容类型标头的人,虽然在较旧的 .NET 版本中,可以使用/sf/answers/2886194741/上的 @erdomke 的答案,但不幸的是,这不再适用于较新的 .NET Core 版本。

以下代码已经过测试,可以与 .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);
}
Run Code Online (Sandbox Code Playgroud)