我试图设置一个HttpClient对象的Content-Type头作为我调用的API所要求的。

我试着像下面这样设置内容类型:

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请求中设置内容类型报头?


当前回答

如果你不介意一个小的库依赖,Flurl。Http[披露:我是作者]使这个超级简单。它的PostJsonAsync方法负责序列化内容和设置内容类型报头,而ReceiveJson则反序列化响应。如果accept头是必需的,你需要自己设置,但Flurl提供了一个非常干净的方式来做到这一点:

using Flurl.Http;

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

Flurl使用HttpClient和Json。它是一个PCL,所以它可以在各种平台上工作。

PM> Install-Package Flurl.Http

其他回答

一些关于。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;

正如预期的那样,我得到了一个聚合异常,其内容为“无法发送具有此动词类型的内容体”。

然而,如果我用。net Core(1.1)做同样的事情-我不会得到异常。我的服务器应用程序非常高兴地回答了我的请求,并选择了内容类型。

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

为那些受困于字符集的人准备的

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

今天我在改变子系统的边缘;从HttpClient转向其他东西,我想到了一些东西…,为什么不用反射来清空“字符集”呢?... 在我尝试之前,我想到了一种方法,“也许我可以在初始化后改变它”,这是有效的。

下面是如何设置“application/json”头没有“;charset = utf - 8”。

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;

注意:下面的空值无效,加";charset = utf - 8”

return new StringContent(jsonRequest, null, "application/json");

EDIT

@DesertFoxAZ建议也可以使用下面的代码,工作良好。(没有自己测试,如果它的工作率,并在评论中赞扬他)

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

对于我的场景,第三方API正在创建HttpRequestMessage,因此我无法使用投票最多的答案来解决这个问题。我不喜欢干扰反射的想法所以其他答案也不成立。

相反,我从AndroidMessageHandler扩展,并使用这个新类作为HttpClient的参数。AndroidMessageHandler包含SendAsync方法,可以在HttpRequestMessage对象发送之前重写该方法。如果您没有访问Android Xamarin库的权限,您可以使用HttpMessageHandler来解决一些问题。

public class XamarinHttpMessageHandler : global::Xamarin.Android.Net.AndroidMessageHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Here I make check that I'm only modifying a specific request
        // and not all of them.
        if (request.RequestUri != null && request.RequestUri.AbsolutePath.EndsWith("download") && request.Content != null)
        {
            request.Content.Headers.Add("Content-Type", "text/plain");
        }
        return base.SendAsync(request, cancellationToken);
    }
}

然后使用:

var client = new HttpClient(new XamarinHttpMessageHandler());

我用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");
        
            ...
        }

这对我很管用。

诀窍在于你可以设置各种标题,比如:

HttpRequestMessage request = new HttpRequestMessage();
request.Headers.Add("Accept-Language", "en"); //works OK

但不是任何标题。例如:

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

将引发运行时异常“误用头名”。这似乎是可行的:

request.Headers.Add(
   HttpRequestHeader.ContentType.ToString(), //useless
   "application/json"
);

但是这样会产生一个名为ContentType的无用头,没有连字符。标头名称不区分大小写,但对连字符非常敏感。

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

string Body = "...";
request.Content =
   new StringContent(Body, Encoding.UTF8, "application/json");

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

Content-Type: application/json; charset=utf-8

在一台没有代理服务器的机器上,使用Fiddler很难发现这一点。Visual Studio曾经有一个网络工具,你可以在其中检查所有的头文件,但只能在版本2015,而不是在更新的版本2017或2022。如果使用调试器检查请求。头文件,你不会发现由StringContent()自动添加的头文件。