HttpClient.PostAsJsonAsync永远不会看到帖子成功和响应的时间

时间:2015-06-04 20:42:34

标签: c# async-await dotnet-httpclient connection-timeout

我们正在使用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 "";
        }

    }

5 个答案:

答案 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应用程序,但.Resultprobably 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;

我们案例中的一行代码解决了这个问题。来自不同团队的开发人员提供了该建议,并且可行。我还没有去谷歌,并阅读它足以提供解释的内容。