Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PostAsync() within Task in .NET 4.0 causing WebException

The goal of the method below is to asynchronously set up and fire an http post from a desktop app to a web controller. I think that there must be a problem with how we set up the Task below, and I believe there are better practices available in .NET 4.5 such as async/await and Task.Run that would address the problem, but upgrading is not currently an option. Is there a better way to handle/write this in .NET 4.0, to prevent the problems described below?

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

That method will sometimes work only 2-3 times, sometimes several, and sometimes it even works for hundreds of posts -- several batches -- before failing. The program continues, but no further posts are reflected in the database, and eventually an exception is thrown. (Possibly due to a timeout.)

We were able to observe this is the exception being thrown:

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

With one inner exception:

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

Interestingly, although the database updates stop after 2-3, the program (an automated batch-processing workflow) continues running and the exception doesn't seem to be thrown until this method is reached to get a new batch. Possibly related?

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

Also I should mention that we've tried putting try/catch in several arrangements, but can't seem to capture that exception anywhere until it bubbles out and breaks the runtime.

(Initially it seemed that the code above was working on our development computers, and failing on production machines, but that turns out to have been random luck combined with a misunderstanding on our part.)

like image 210
RJB Avatar asked Nov 02 '22 06:11

RJB


1 Answers

This code seems to have solved it.....

    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());
    }
like image 194
RJB Avatar answered Nov 04 '22 09:11

RJB