HttpClient.PostAsJsonAsync never sees when the post is succeeding and responding

断了今生、忘了曾经 提交于 2021-02-07 11:28:36

问题


We are using an HttpClient to post json to a restful web service. In one instance, we are running into something that has us baffled. Using tools like postman, fiddler etc, we can post to an endpoint and see that it is working. When we do the same with HttpClient.PostAsJsonAsync, we can verify in the software we are posting to that it received the data just fine. However, our PostAsJsonAsync will always eventually time out rather than give us a response.

We have worked with the team that created the service we are consuming, plus our additional testing on our side, and we have not yet been able to truly time out that service.

Every time we do a post with HttpClient, we then can verify that the target software we post to does indeed get the data. Any time we do a post to that target software from any other tool, we always very quickly see a response with status code of 200. Something about HttpClient is failing to accept the response from this particular service. Does anyone have an idea what we can look at from here?

Here's the code (though it is so cookie cutter that I hardly feel it is needed)

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 "";
        }

    }

回答1:


This:

var response = client.PostAsJsonAsync(useUrl, o).Result;

Is causing you code to deadlock. This is often the case when blocking on async API's, and that's why you're experiencing the "I don't see any response coming back" effect.

How is this causing a deadlock? The fact that you are executing this request in an environment that contains a synchronization context, perhaps one which belongs to the UI. It's executing the async request, and when the response arrives, it continues via an IO completion thread which attempts to post the continuation onto that same UI context, which is currently blocked by your .Result call.

If you want to make an HTTP request synchronously, use WebClient instead. If you want to take advantage of the async API properly, then await instead of block with .Result.




回答2:


I had the same issue and this SO answer fixed it for me.

In a nutshell, you have to use the ConfigureAwait(false) extension to avoid the deadlock:

var response = await client.PostAsJsonAsync(useUrl, o).ConfigureAwait(false);



回答3:


Is there a reason why you're not following the async await pattern? You're calling an async method, but not awaiting it. You didn't say if the code calling your REST service was a Windows Forms or ASP.NET application, but that .Result is probably causing you issues.

Can you restructure your method like this:

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 "";
    }

}



回答4:


This is a slight modification to @Justin Helgerson's solution. There are 2 blocking .Result calls in your method; once you go async you should fix them both.

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 "";
    }
}

Note I've also renamed the method to PostDataAsync in accordance with the TAP pattern.




回答5:


System.Net.ServicePointManager.Expect100Continue = false;

That one line of code in our case fixed the problem. A developer from a different team offered that suggestion, and it works. I have yet to google it and read up on it enough to offer an explanation of what that is addressing.



来源:https://stackoverflow.com/questions/30653770/httpclient-postasjsonasync-never-sees-when-the-post-is-succeeding-and-responding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!