我试图设置一个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请求中设置内容类型报头?
微软似乎试图强迫开发人员遵循他们的标准,甚至没有提供任何选项或设置,特别是考虑到这是一个客户端,我们是由服务器端需求决定的,特别是考虑到微软服务器端框架本身需要它!
所以基本上微软试图强迫我们养成好习惯,当连接到他们的服务器技术时,强迫我们养成不好的习惯……
如果微软的任何人正在阅读这篇文章,那么请修复它…
无论哪种方式,对于任何需要Get等内容类型头的人来说,在旧的。net版本中,可以在https://stackoverflow.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);
}
内容类型是内容的头,而不是请求的头,这就是失败的原因。Robert Levy建议的AddWithoutValidation可以工作,但是你也可以在创建请求内容本身时设置内容类型(注意,代码片段在两个地方添加了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);
});
我用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()自动添加的头文件。