在.NET 4.0中,Task中的PostAsync()导致WebException

5
下面的方法旨在从桌面应用程序异步设置和触发向Web控制器的http post。我认为我们在下面的任务设置上一定存在问题,我相信在.NET 4.5中有更好的实践方法,如async/await和Task.Run,可以解决这个问题,但目前升级不是一个选择。在.NET 4.0中,有更好的处理/编写此代码的方法来防止以下问题吗?
    public void PostWithoutResponse(object objectToPost, string url) {
        Task.Factory.StartNew(() =>
        {
            using (var handler = new HttpClientHandler()) {
                handler.PreAuthenticate = true;
                handler.Credentials = _credentialPool.GetNetworkCredentials(new Uri(url));
                using (var client = new HttpClient(handler)) {
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    using (var stringContent = new StringContent(JsonConvert.SerializeObject(objectToPost), Encoding.UTF8, "application/json")) {
                        // We weren't able to get this post to work without waiting for result
                        var result = client.PostAsync(url, stringContent).Result;
                    }
                }
            }
        });
    }

这种方法有时只能工作2-3次,有时会有几个批次,有时甚至可以在数百篇文章中(几批次)工作几次,然后失败。程序继续执行,但数据库中不再反映任何其他帖子,并最终抛出异常(可能是由于超时)。

我们能够观察到产生的异常是:

System.AggregateException was unhandled
Message: An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll
Additional information: One or more errors occurred.

除了一个内部异常:

_innerException {"The request was canceled"}    System.Exception {System.Net.WebException}

有趣的是,尽管数据库更新在2-3次后停止,但程序(自动批处理工作流)继续运行,直到达到获取新批次的方法时才抛出异常。可能有关联吗?
    public string GetPostResult(object objectToPost, string url) {
        string jsonResult = null;
        using (var handler = new HttpClientHandler()) {
            handler.PreAuthenticate = true;
            handler.Credentials = _credentialPool.GetNetworkCredentials(new Uri(url));
            using (var client = new HttpClient(handler)) {
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                var serializedContent = JsonConvert.SerializeObject(objectToPost);
                using (var stringContent = new StringContent(serializedContent, Encoding.UTF8, "application/json")) {
                    var taskResult = client.PostAsync(url, stringContent).Result;
                    jsonResult = taskResult.Content.ReadAsStringAsync().Result;
                }
            }
        }
        return jsonResult;
    }

我应该提到的是,我们尝试了多种try/catch的排列方式,但似乎无法捕获异常,直到它冒泡出来并导致运行时崩溃。

(最初看起来,上面的代码在我们的开发计算机上运行良好,在生产机器上失败,但事实证明这只是幸运的随机事件结合我们的误解。)


听起来你可能在过快地访问服务器,被限制了访问速度。 - Steve Czetty
@RJB 因为你的机器上设置的 IIS 没有启用速率限制吗? - Scott Chamberlain
1
请参见https://dev59.com/AXE95IYBdhLWcg3wGaB_。 - Peter Ritchie
@RJB,你是否正在使用最新的HttpClient:http://www.nuget.org/packages/Microsoft.Net.Http? - noseratio - open to work
1
那么也许您应该考虑使用普通的 WebRequest,并设置其 webRequest.KeepAlive = false,正如 @PeterRitchie 所示的链接。 您已经没有在现有代码中利用异步 HttpClient 方法的优势。 - noseratio - open to work
显示剩余3条评论
1个回答

2

这段代码似乎解决了它......

    public void PostWithoutResponse(object objectToPost, string url) {
        var uri = new Uri(url);
        var httpPost = (HttpWebRequest)WebRequest.Create(uri);
        httpPost.KeepAlive = false;
        httpPost.Method = "POST";
        httpPost.Credentials = _credentialPool.GetNetworkCredentials(uri);
        httpPost.ContentType = "application/json";
        using (var streamWriter = new StreamWriter(httpPost.GetRequestStream())) {
            var json = JsonConvert.SerializeObject(objectToPost);
            streamWriter.Write(json);
            streamWriter.Flush();
            streamWriter.Close();
        }
        Task.Factory.StartNew(() => httpPost.GetResponse());
    }

RJB,您确定最后一行不应该只是return httpPost.GetResponse(),而没有Task.Factory.StartNew吗? - noseratio - open to work
我先尝试了那种方式,但是方法在“.GetResponse()”上同步阻塞,这种方式更加方便且不需要等待。到目前为止已经成功处理了数千个帖子。 - RJB
虽然也许可以为 Task.Factory.FromAsync<WebResponse>(httpPost.BeginGetResponse, httpPost.EndGetResponse, null); 提出一些论点。嗯。 - RJB
是的,你可以使用FromAsync甚至添加一些重试逻辑:https://dev59.com/1Xvaa4cB1Zd3GeqPG7lU#21346870,但几乎永远不应该做到“fire-and-forget”,因为你的桌面应用程序将无法观察到任何错误。 - noseratio - open to work

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接