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


当前回答

对于我的场景,第三方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());

其他回答

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

尝试使用TryAddWithoutValidation

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

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

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()自动添加的头文件。

调用AddWithoutValidation而不是Add(参见MSDN链接)。

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

对于那些想要专门将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://learn.microsoft.com/en-us/previous-versions/aspnet/hh944339(v=vs.118)