我们正在使用HttpClient将json发布到一个安静的Web服务。在一个例子中,我们遇到了困扰我们的事情。使用postman,fiddler等工具,我们可以发布到端点并看到它正在工作。当我们对HttpClient.PostAsJsonAsync做同样的事情时,我们可以在我们发布的软件中验证它收到的数据就好了。但是,我们的PostAsJsonAsync最终会超时而不是给我们回复。
我们与创建我们正在消费的服务的团队合作,加上我们的额外测试,我们还没有真正超时的服务。
每次我们使用HttpClient发布帖子时,我们都可以验证我们发布的目标软件是否确实获得了数据。每当我们从任何其他工具发布到目标软件的帖子时,我们总是很快看到状态代码为200的响应。关于HttpClient的某些内容未能接受来自此特定服务的响应。有没有人知道我们可以从这里看到什么?
这里是代码(虽然它是如此奇特的切割,我几乎觉得不需要)
public string PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = client.PostAsJsonAsync(useUrl, o).Result;
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return response.Content.ReadAsStringAsync().Result;
}
return "";
}
}
答案 0 :(得分:9)
此:
var response = client.PostAsJsonAsync(useUrl, o).Result;
导致代码死锁。阻止异步API的情况通常就是这种情况,这就是为什么你会遇到"我没有看到任何回复的反应" 效果。
这是如何导致死锁的?您在包含同步上下文的环境中执行此请求的事实,可能是属于UI的同步上下文。它正在执行异步请求,当响应到达时,它会继续通过IO完成线程尝试将延续发布到同一UI上下文中,该上下文当前被.Result
调用阻止。
如果要同步发出HTTP请求,请改用WebClient
。如果您想要正确利用异步API,那么await
代替使用.Result
阻止。
答案 1 :(得分:5)
我遇到了同样的问题,this SO answer为我修好了。
简而言之,您必须使用ConfigureAwait(false)
扩展名来避免死锁:
var response = await client.PostAsJsonAsync(useUrl, o).ConfigureAwait(false);
答案 2 :(得分:4)
您是否有理由不遵循异步等待模式?您正在调用异步方法,但不等待它。您没有说出调用REST服务的代码是Windows窗体还是ASP.NET应用程序,但.Result
是probably causing you issues。
你可以像这样重构你的方法:
public async Task<string> PostData(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = await client.PostAsJsonAsync(useUrl, o);
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return await response.Content.ReadAsStringAsync();
}
return "";
}
}
答案 3 :(得分:1)
这是@Justin Helgerson解决方案的略微修改。您的方法中有2个阻止sc.textFile("yourfile.txt")
.distinct
.saveAsTextFile("distinct.txt")
次调用;一旦你去异步,你应该修复它们。
.Result
注意我还根据TAP pattern将方法重命名为public async Task<string> PostDataAsync(string resourcePath, Object o, Boolean isCompleteUrl = false, int timeoutMinutes = -1)
{
using (var client = new HttpClient())
{
if (timeoutMinutes > 0)
{
client.Timeout = new TimeSpan(0,timeoutMinutes,0);
}
var useUrl = isCompleteUrl ? resourcePath : ApiBase + resourcePath;
var response = await client.PostAsJsonAsync(useUrl, o);
if(response.StatusCode == System.Net.HttpStatusCode.OK)
{
return await response.Content.ReadAsStringAsync();
}
return "";
}
}
。
答案 4 :(得分:1)
System.Net.ServicePointManager.Expect100Continue = false;
我们案例中的一行代码解决了这个问题。来自不同团队的开发人员提供了该建议,并且可行。我还没有去谷歌,并阅读它足以提供解释的内容。