【问题标题】:Explicitly Set Content-Type Headers For Get Operation in HttpClient在 HttpClient 中为 Get 操作显式设置 Content-Type 标头
【发布时间】:2014-09-29 22:04:00
【问题描述】:

在使用HttpClient 执行GET 时,有没有一种方法可以显式设置Content-Type 标头值?

我意识到这违反了 1.1 协议,但我正在使用一个不符合它的 API,并且需要我设置一个 Content-Type 标头。

我试过了,没用...

using (var httpClient = new HttpClient())
{
   var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://example.com");

   httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded+v1.3");

   await httpClient.SendAsync(httpRequestMessage)
}

在添加TryAddWithoutValidation 后,我检查了DefaultRequestHeaders,它似乎没有设置Content-Type 值。

如果我尝试设置 httpRequestMessage 的 Content-Type(通过设置 httpRequestMessage.Content = ...,我会收到以下错误:

Cannot send a content-body with this verb-type.

有没有一种方法可以让我使用 HttpClient 为 GET 操作显式设置 Content-Type?

【问题讨论】:

  • 作为一种解决方法,我同时可以使用WebClient。
  • @ZenCoder。这不会设置 Content-Type 标头。只有Accept 标头。
  • GET 操作没有正文,因此无需设置 Content-Type。如果可能,应更正 API。

标签: c# .net dotnet-httpclient


【解决方案1】:

根据我的发现,我得出结论,HttpClient 在协议规则方面非常严格。我还通过实现 DLL 进行了反映,但我找不到任何表明它允许违反协议的内容。

GET 请求不应包含内容类型的标头,并且 HttpClient 正在执行该规则。

我认为当您尝试设置内容类型标头时的异常消息是自我描述的:

System.InvalidOperationException:错误的标头名称。确保请求标头与 HttpRequestMessage 一起使用,响应标头与 HttpResponseMessage 一起使用,内容标头与 HttpContent 对象一起使用。

此外,如果您使用设置内容正文,您还会收到一条自我描述的消息:

System.Net.ProtocolViolationException:无法发送具有此动词类型的内容主体。

由于您愿意为 GET 请求违反 HTTP 规则,我很确定您唯一的选择是坚持使用限制较少的 WebClient,它适用于该场景。

【讨论】:

  • 是的,对不起,我需要重复一遍——我正在集成的 API 不符合 HTTP 规范。它们需要 GET 操作,提供 Content-Type 标头。
  • 对。对不起。我已经重写了我的回复。
  • 谢谢,我感觉这可能是唯一的选择。感谢您的帮助
【解决方案2】:

通过一些反射和引入DelegatingHandler 作为参数提供给HttpClient 构造函数来覆盖库行为是可能的——而且非常肮脏。请参阅下面的代码。

public class HmacAuthenticatingHandler : DelegatingHandler
{
    public HmacAuthenticatingHandler(HttpMessageHandler innerHandler) 
       : base(innerHandler)
    {
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {

        // HACK: Set 'Content-Type' even for GET requests
        var invalidHeaders = (HashSet<string>)typeof(HttpHeaders)
            // use "_invalidHeaders" for System.Net.Http v2.2+
            .GetField("invalidHeaders", BindingFlags.NonPublic | BindingFlags.Instance)
            .GetValue(request.Headers);
        invalidHeaders.Remove("Content-Type");

        request.Headers.Remove("Content-Type");
        request.Headers.Add("Content-Type", "application/json");

        var response = await base.SendAsync(request, cancellationToken);
        return response;
    }
}

【讨论】:

  • 不适用于 .Net Framework 4.0 版、System.Net.Http 2.2.29.0 版但可用于 2.0.0.0
  • @prem 将“invalidHeaders”更改为“_invalidHeaders”
【解决方案3】:

虽然现在对您没有帮助,但看起来 .NET 框架的未来版本可能会通过添加 AddWithoutValidation 方法来支持这种类型的协议违规:

https://msdn.microsoft.com/en-us/library/hh204926

【讨论】:

    【解决方案4】:

    您是否尝试将标头添加到内容标头(与请求标头并置) 见here

    【讨论】:

      【解决方案5】:

      我在需要调用 API 时遇到了同样的情况,我能够通过将内容设置为空的 StringContent 来解决它:

      httpRequestMessage.Content = new StringContent("", Encoding.ASCII, "application/json");
      

      这会发送一个Content-Type 和一个Content-Length 标头(值为0),我调用的API 可以使用。如果 API 拒绝带有 Content-Length 标头的请求,这将不起作用。

      我正在使用 .NET Core 3.1。看起来 OP 使用的版本不支持在 GET 请求上设置 Content 属性。

      【讨论】:

        猜你喜欢
        • 2012-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多