HttpRequestException - 这是客户端还是服务器问题?
Posted
技术标签:
【中文标题】HttpRequestException - 这是客户端还是服务器问题?【英文标题】:HttpRequestException -- Is this a client or server issue? 【发布时间】:2016-12-01 14:42:46 【问题描述】:不久前,我使用 HttpClient
类实现了一些代码来使用 REST Api。
using (var client = new HttpClient() BaseAddress = new Uri(@"https://thirdparty.com") )
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(...);
var uri = new Uri(@"rest/api/foo", UriKind.Relative);
var content = new StringContent(json.ToString());
using (var response = await client.PostAsync(uri, content))
// etc ...
这段代码似乎在测试和生产环境(每个环境都访问一个测试/生产 uri)上都可以正常工作。最近,我们开始在生产环境中得到一个HttpRequestException:System.Net.Http.HttpRequestException: Error while copying content to a stream.
这似乎有点奇怪,所以我使用 Postman 发送相同的消息,它工作得很好。我不确定为什么我们的代码失败而 Postman 正在工作。我更改了 json 数据中的一个参数(从“NY”到“NV”的状态),我们的 .NET 代码运行良好——当然我们不能只发送错误的 json 数据,所以这不是解决方案;这更像是一种观察,即完全相同的代码适用于不同的内容。
有趣的是,我们可以进行两个代码更改来解决这个问题。首先,Postman 能够使用 RestSharp
包生成工作 C# 代码。或者,我从另一个指向使用 HttpVersion 1.0 的问题中找到了 answer:
using (var request = new HttpRequestMessage(HttpMethod.Post, uri))
request.Version = HttpVersion.Version10;
request.Content = new StringContent(json.ToString());
using (var response = await client.SendAsync(request))
// etc ...
令人困惑的部分是 Postman 使用的是 HTTP/1.1 版本。所以,总结一下:
如果我们更改 json 数据(美国州从“NY”改为“NV”),代码就可以工作。 相同的 json 和代码适用于测试 uri。 更改代码以使用 RestSharp 包有效。 将代码更改为使用 HTTP/1.0 而不是 HTTP/1.1 有效。到底为什么 Postman 能够使用 HTTP/1.1 工作,但 HttpClient 却失败了?这是我们客户的问题吗(该代码适用于美国其他州)?这是 .NET Framework 中的错误吗?第三方的 REST Api 的实现/托管是否有问题?
邮递员标题:
POST rest/api/foo HTTP/1.1
Host: thirdparty.com
Content-Type: application/json
Authorization: Basic SOME_ENCRYPTED_USER_PASS
Cache-Control: no-cache
Postman-Token: 2fa5b5a0-b5d3-bd4c-40f0-d2b55b60316b
示例 Json:
"stateCode": "NY",
"packageID": "58330",
"name": "58330-PRI-1",
"documents": [
"type": "SPECIAL",
"name": "Sample Document",
"documentID": "3569976"
],
"descriptions": [
"city": "New York",
"state": "NY"
]
堆栈跟踪:
AggregateException: One or more errors occured.
HttpRequestException: Error while copying content to a stream.
IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
SocketException: An existing connection was forcibly closed by the remote host.
【问题讨论】:
postman 的 body 是否和使用 client.PostAsync() 发送的 body 完全一样?我相信版本之间的区别在于 keep-alive 标头,因此 Postman 处理超时的方式可能与 .NET 不同。 @NickSpicer:正确,正文是相同的json数据。而且我相信是 HTTP/1.1 与 HTTP/1.0 决定了连接保持活动与关闭的事实。我已经更新了我的答案以添加邮递员标题数据。 你一直在使用 async/await 吗?当您将 async/await 调用与其正常的同步对应调用混合时,就会发生奇怪的事情。 当您更改数据时它起作用的事实似乎证明服务器代码存在问题。我认为这与 HTTP 版本(1.0 与 1.1)无关,或者您有一个非常奇怪的服务器破坏了 HTTP 级别。 请将异常的完整堆栈跟踪添加到您的问题中。可能有我有用的内部异常。 【参考方案1】:您在运行 HttpClient 的机器上安装了防病毒/防火墙吗?我过去曾遇到过 AVG、Mcafee、Norton 和其他静默阻止请求的问题。找出确切的位置非常棘手,但可能有一个监视端口的选项卡,取消勾选/禁用它可能有助于识别问题。如果是这种情况,正确的解决方案是将您的“thirdparty.com”列入相应供应商的白名单。
是否也值得查看来自服务器的响应标头?您可以将它们添加到您的问题中吗?仅因为过去我发现 Content-Security-Policy 标头阻止了我的某些请求完成?
【讨论】:
【参考方案2】:由于您可以稍微更改数据并使其成功,我想说您的问题与您的任何代码都无关。如果他们的服务器缓存了一个错误的值,并且会继续向您发送该值,直到他们的缓存被清除,该怎么办?
尝试隐式告诉服务器不要使用缓存值...
using (var client = new HttpClient() BaseAddress = new Uri(@"https://thirdparty.com") )
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(...);
var uri = new Uri(@"rest/api/foo", UriKind.Relative);
var content = new StringContent(json.ToString());
client.DefaultRequestHeaders.CacheControl = CacheControlHeaderValue.Parse("no-cache");
using (var response = await client.PostAsync(uri, content))
// etc ...
看看你是否得到一个有效的响应流。
【讨论】:
可悲的是我得到了同样的例外。"no-cache"
选项没有改变任何东西。
您使用的是 response.Content.ReadAsStringAsync() 还是 ReadAsStreamAsync/ReadAsByteArrayAsync ?
我不确定这有什么关系,因为在执行client.PostAsync(uri, content)
的行上抛出了异常?之后的一切都不重要了。
只需阅读一些关于 BaseAddress 的内容,该内容需要在末尾添加一个正斜杠。如果这是您的问题,我会感到惊讶,因为代码适用于不同的数据。只需提及它即可将其从列表中删除。
我要检查的一件事是带有“NY”与“NV”的 json.ToString() 值,看看这组特定数据是否存在一些奇怪的格式问题。另外我会尝试设置内容类型: content.Headers.ContentType = new MediaTypeHeaderValue("application/json");以上是关于HttpRequestException - 这是客户端还是服务器问题?的主要内容,如果未能解决你的问题,请参考以下文章
使用 HttpRequestException 获取失败请求的响应正文
Polly HandleTransientHttpError 未捕获 HttpRequestException
EnsureSuccessStatusCode 的使用和它抛出的 HttpRequestException 的处理
从 C# 调用 WebApi 获取异常 HttpRequestException