使用HttpClient提交JSON至服务器为空及相关排查疑问
咱们一步步来拆解你遇到的这些问题,帮你逐个理清:
首先,你用httpbin.org测试的结果已经说明:HttpClient发送的请求是带有正确body的——返回里的data字段就是你发送的JSON字符串,json字段是解析后的结果。那问题大概率出在你的服务器端:
- 可能服务器没正确处理
Expect: 100-continue头:HttpClient默认会发送这个头,意思是先询问服务器是否准备好接收数据,等服务器返回100响应后才会发送body。如果你的服务器不支持或没正确处理这个头,就可能出现服务器只收到头、没收到body的情况。可以试试禁用这个头:client.DefaultRequestHeaders.ExpectContinue = false; - 可能服务器日志没打印body:很多服务器默认只输出请求头,你看到的报文只是头部分,实际body已经发送过去了。建议在服务器端加代码主动读取并打印请求body,验证是否真的为空。
StringContent(以及所有HttpContent子类)的内容是以流的形式存储的,而且默认只能读取一次。调试时直接看对象看不到内容,是因为流还没被读取。要验证内容是否正确,可以这样做:
// 异步方式(推荐) string contentStr = await httpContent.ReadAsStringAsync(); Console.WriteLine(contentStr); // 同步方式(适配你的代码场景) string contentStr = httpContent.ReadAsStringAsync().Result;
注意:一旦读取过流,原HttpContent就不能再用于发送请求了(流位置到末尾了)。如果想调试后还能正常发送,可以先把内容缓存到内存:
httpContent.LoadIntoBufferAsync().Wait(); // 将内容缓存到内存,支持多次读取 string contentStr = httpContent.ReadAsStringAsync().Result; // 之后httpContent仍可正常用于PostAsync请求
结合httpbin的测试结果,说明body确实发送了,只是你看到的服务器日志只显示了请求头,没包含body部分。很多服务器的日志组件默认不输出请求body,需要单独配置开启。
另外对比RestSharp和Postman的请求:Postman的报文里显示了body,是因为它的日志默认包含body,而你的服务器日志没做这个配置。
你误以为看不到payload,但其实httpbin返回的data字段就是你的请求payload(也就是请求body)。比如你发送的{"name":"listFiles"},在返回里data就是这个字符串,json字段是httpbin帮你解析后的JSON对象——这和你看的文档描述的payload完全一致,只是展示形式不同而已。
Connection: Keep-Alive:客户端告诉服务器,请求完成后不要关闭TCP连接,后续请求可以复用这个连接,减少TCP握手的开销,提升性能。HttpClient默认会使用这个配置。Connection: Close:客户端告诉服务器,请求完成后关闭TCP连接,下次请求需要重新建立连接。httpbin返回这个头是它自己的服务器配置,和你的请求无关。
这个区别本身不会导致服务器收不到body,除非你的服务器对Keep-Alive连接的处理有bug,比如没有正确读取完连接上的请求数据。可以试试强制关闭连接验证:
client.DefaultRequestHeaders.ConnectionClose = true;
内容的提问来源于stack exchange,提问作者mans

