HttpRequestMessage的Content-Type头部的奇怪行为
HttpRequestMessage的Content-Type头部的奇怪行为
我尝试使用C#(.NET Core 2.2.104)向外部 Web 服务发送带有 application/json 格式的请求体的 HTTP POST 请求。我已经阅读了 Stack Overflow 上所有类似的问题,并编写了以下代码:
SignXmlRequestDto requestBody = new SignXmlRequestDto(p12, model.SignCertPin, model.Data); string json = JsonConvert.SerializeObject(requestBody); var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = ncanNodeUrl, Headers = { { HttpRequestHeader.ContentType.ToString(), "application/json" } }, Content = new StringContent(JsonConvert.SerializeObject(json)) }; var response = await httpClient.SendAsync(httpRequestMessage); string responseString = await response.Content.ReadAsStringAsync();
我从服务端收到了一个错误,错误信息是:“无效的 Content-Type 标头,请将 Content-Type 设置为 application/json”。有趣的是,如果我使用 Postman 模拟该请求,一切都正常,并且我收到了成功的响应。[点击此处查看截图](https://i.stack.imgur.com/4SBm5.png)
更新:根据 @Kristóf Tóth 的建议,我修改了代码如下:
var httpRequestMessage = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = ncanNodeUrl, Content = new StringContent(json, Encoding.UTF8, "application/json") }; var response = await httpClient.SendAsync(httpRequestMessage); string responseString = await response.Content.ReadAsStringAsync();
但是仍然收到了相同的错误消息。
问题的出现原因是Content-Type头部未被正确设置。解决方法是使用JsonContent代替StringContent,并将内容序列化为JSON字符串后再进行包装。具体方法如下:
// 将内容序列化为JSON字符串 var stringPayload = JsonConvert.SerializeObject(payload); // 使用HttpClient类对JSON字符串进行包装,设置Content-Type为application/json var httpContent = new StringContent(stringPayload, Encoding.UTF8, "application/json");
另外,也某些情况下这种方法无法解决问题,仍然出现相同的错误。而有人回答说这是HTTP POST请求的正确方法,如果这种方法不起作用,那么在HttpClient发布4年后人们应该已经注意到了。
从上面的内容中可以整理出以下问题的原因和解决方法:
问题原因:
问题出现在HttpRequestMessage的Content-Type头部。Content-Type应该设置在内容上,而不是请求本身。通过设置StringContent的Headers.ContentType属性可以解决这个问题。但是使用StringContent的构造函数设置Content-Type并没有起作用。
解决方法:
可以通过以下两种方法来设置Content-Type:
1. 使用StringContent的构造函数:
Content = new StringContent(JsonConvert.SerializeObject(json),Encoding.UTF8, "application/json")
2. 设置StringContent的Headers.ContentType属性:
var content=new StringContent(JsonConvert.SerializeObject(json)); content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
在这里,第二种方法解决了问题。但是为什么第一种方法没有起作用呢?通过使用Fiddler来检查有什么不同。Postman可以帮助手动创建HTTP请求,但无法显示实际发送或接收的内容。Fiddler可以捕获两者。
此外,还需要注意使用的.NET版本和通过NuGet包添加的HttpClient。通过构造函数传递Content-Type非常常见,所以人们应该已经注意到了。可能存在某些隐秘的错误组合,例如在特定的HttpClient版本或.NET Core运行时中,但这仍然是非常不可能的。
作者使用的是.NET Core 2.2.104版本,并尝试使用了stackoverflow上的代码,但并没有解决问题。