HttpWebRequest randomly fails with async and await

2019-07-08 09:40发布

问题:

I would like to do serveral POST-Request.

My problem is that the Application crashes randomly in the Task<...> do_POST()-Function during await in my async void bt_publishPath_Click()-Event.

I can do some POST-Requests (everything's working fine) and sometime the await will block and the app crashes with a System.NullReferenceException (after a long time of waiting). I don't understand why this is happing because the first times it is working.

Here is what I got so far:

I have commented out every unimportant functionality. I have tested my code without these lines of code and the error occures, too.

// MainWindow
private async void bt_publishPath_Click(object sender, RoutedEventArgs e)
{
    // gr_spinner.Visibility = Visibility.Visible;

    ApiResponse res = await ApiCall.do_POST(
        "/path",
        getBasicAuthString(),
        flink.getJsonString()
    );

    // gr_spinner.Visibility = Visibility.Collapsed;
    // Globals.errorHandling(res.Statuscode, res.Parameters[0]);
}

// ApiCall static Class
public static Task<ApiResponse> do_POST(string apiPath, string auth, string json)
{
    return Task.Run(() =>
    {
        int statuscode = 0;
        HttpWebResponse response = null;
        try
        {
            // HEADER
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiBase + apiPath);
            request.Method = "POST";
            request.ContentType = "application/json";
            request.Headers.Add("Authorization", auth);

            // DATA
            byte[] data = Encoding.ASCII.GetBytes(json);
            request.ContentLength = data.Length;
            Stream requestStream = request.GetRequestStream();
            requestStream.Write(data, 0, data.Length);
            requestStream.Close();

            // SEND AND GET RESPONSE
            /*
             * It seems to be that GetResponse() isn't called correctly.
             * The Backend doesn't receive a POST-request and the reponse stays null.
             */
            response = (HttpWebResponse)request.GetResponse();
            statuscode = (int)response.StatusCode;
        }
        catch (WebException ex)
        {
            statuscode = (int)((HttpWebResponse)ex.Response).StatusCode;
        }
        return new ApiResponse(statuscode, null, new String[] { apiPath });
    });
}

Additional information:

Before doing this with async and await I tried to solve this issue by using a BackgroundWorker. In this case I had a similar problem: Sometimes the backgroundWorker of the previous POST was still busy and the program wasn't able to start a new one (deadlock).

Furthermore I have found something interesting: async await Task null reference exception

  • But in this case it is a ASP.NET-Application. I am developing a Windows-Desktop client - not a server application. So this doesn't really help.

Thank you for your help!

回答1:

In your catch block you have (HttpWebResponse)ex.Response.

From MSDN:

If a response is available from the Internet resource, a WebResponse instance that contains the error response from an Internet resource; otherwise, null.

So your problem is a non-responsive server and an error in your error handling.
But don't fix this, look up the HttpClient and write a new async Task<> DoPost(...).



回答2:

SOLUTION

Big thanks to Henk Holterman. He gave me advice to use HttpClient instead of HttpWebRequest. For everyone having the same problem like me: You will get HttpClient as a NuGet-Package.

ApiCall.cs:

private static HttpClient httpClient = new HttpClient();

public static async Task<HttpResponseMessage> do_POST(string apiPath, string auth, string json)
{
    HttpResponseMessage response;
    StringContent request = new StringContent(json);
    request.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", auth);
    try
    {
        response = await httpClient.PostAsync(new Uri(apiBase + apiPath), request);
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine(ex.Message);
        return null;
    }
    return response;
}

MainWindow.cs:

private async void bt_publishPath_Click(object sender, RoutedEventArgs e)
{
    // [...]
    HttpResponseMessage res = await ApiCall.do_POST(
        "/path",
        "admin:123456",
        "{ \"hello\" : \"world\" }"
    );
    // [...]
}