我正在尝试从在IIS 8.5上运行的ASP.NET应用程序内对另一台服务器进行HTTP调用。
首先,我从Microsoft的Call a Web API From a .NET Client (C#)的文章中获得了一些提示。 我可以很容易地看到它们在此处进行HTTP调用的方式。仅显示一个简化的示例:
static async Task<Product> GetProductAsync(string path)
{
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
// retrieve response payload
... = await response.Content.ReadAsAsync<...>();
}
// do something with data
}
我想这很容易,所以我很快为我的应用程序编写了一个类似的方法(请注意,ReadAsAsync
扩展方法appears to require an additional library,所以我选择了一种更抽象的内置方法,但是否则可能是类似的方法):
private async Task<MyInfo> RetrieveMyInfoAsync(String url)
{
var response = await HttpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var responseBody = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<MyInfo>(responseBody);
}
不幸的是,调用此方法将导致我的应用程序挂起。调试时,事实证明对await
的{{1}}调用永远不会返回。
经过一番搜索,我偶然发现了remotely similar issue,在其评论部分中,我通过a very interesting suggestion找到了Mr. B:
删除所有异步内容并确保其有效。
所以我尝试了:
GetAsync
(对我而言),这确实令人惊讶。 private Task<MyInfo> RetrieveMyInfoAsync(String url)
{
return HttpClient.GetAsync(url).ContinueWith(response =>
{
response.Result.EnsureSuccessStatusCode();
return response.Result.Content.ReadAsStringAsync();
}).ContinueWith(str => JsonConvert.DeserializeObject<MyInfo>(str.Result.Result));
}
在不到一秒钟的时间内从另一台服务器返回了预期的响应。
现在,同时使用AngularJS,我对GetAsync
和response.Result.Content
之类的东西感到有些失望。在AngularJS中,我希望上面的调用就像这样:
str.Result.Result
即使我们不喜欢JavaScript中发生的自动JSON反序列化,AngularJS代码仍然更容易实现,例如$http.get(url).then(function (response) {
return response.data;
});
并没有包装在一个Promise之类的东西中,当从延续函数中返回另一个Promise时,我也不会得到像response
这样的结构。
因此,如果使用Task<Task<...>>
语法而不是更具可读性的async-await模式(如果后者有效的话),我感到不满意。
我的C#HTTP调用的async-await变体在做什么?
答案 0 :(得分:4)
因此,从ConfigureAwait(false)
有助于解决您的问题的角度来看,请阅读Stephen Cleary博客中的内容:
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
这家伙几乎是async
专家(他写了《 C#Cookbook中的并发性》一书),所以我能说的他可能解释得更好。基本上,您是在某个地方阻塞ASP.NET线程,可能不是一直使用await
,而是使用Wait
,Result
或GetResult()
。您应该可以使用该博客自己诊断问题。
ConfigureAwait(false)
所做的是,它无法捕获当前上下文,因此HTTP请求在ASP.NET上下文以外的其他位置(正确)执行,从而避免了死锁。
编辑:
根据您的评论, GetAwaiter().GetResult()
是导致此问题的原因。如果将其更改为await
,并将调用方法更改为async
,则可能会修复所有问题。
自从C#7.0和async Task Main()
方法支持以来,实际上根本没有理由阻塞而不是在应用程序代码中使用await
。