我试图设置一个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请求中设置内容类型报头?
尝试使用HttpClientFactory
services.AddSingleton<WebRequestXXX>()
.AddHttpClient<WebRequestXXX>("ClientX", config =>
{
config.BaseAddress = new System.Uri("https://jsonplaceholder.typicode.com");
config.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
config.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json; charset=utf-8");
});
======================
public class WebRequestXXXX
{
private readonly IHttpClientFactory _httpClientFactory;
public WebRequestXXXX(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public List<Posts> GetAllPosts()
{
using (var _client = _httpClientFactory.CreateClient("ClientX"))
{
var response = _client.GetAsync("/posts").Result;
if (response.IsSuccessStatusCode)
{
var itemString = response.Content.ReadAsStringAsync().Result;
var itemJson = System.Text.Json.JsonSerializer.Deserialize<List<Posts>>(itemString,
new System.Text.Json.JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return itemJson;
}
else
{
return new List<Posts>();
}
}
}
}
内容类型是内容的头,而不是请求的头,这就是失败的原因。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);
});
. net试图强迫你遵守某些标准,即content - type头只能在有内容的请求上指定(例如POST, PUT等)。因此,正如其他人指出的那样,设置Content-Type报头的首选方法是通过HttpContent.Headers.ContentType属性。
话虽如此,某些Api(如截至2016-12-19的LiquidFiles Api)需要为GET请求设置Content-Type报头,. net不允许在请求本身设置此报头——即使使用TryAddWithoutValidation。此外,您不能为请求指定Content——即使它的长度为零。我唯一能避开这个问题的方法似乎就是诉诸于反思。代码(以防其他人需要它)是
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");
编辑:
正如注释中所指出的,该字段在不同版本的dll中有不同的名称。在GitHub的源代码中,该字段当前命名为s_invalidHeaders。这个例子已经根据@David Thompson的建议进行了修改。
对于我的场景,第三方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());
诀窍在于你可以设置各种标题,比如:
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()自动添加的头文件。