0

I'm trying to send a HTTP POST request multiple times but not in parallel. I've created another class and put this function into it:

    public async Task<string> sendReqAsync()
            {
               
                requestTime = DateTime.Now;
                var task = await client.PostAsync("https://api.example.com", content).ContinueWith(async (result) =>
                {
                    responseTime = DateTime.Now;
                    return await result.Result.Content.ReadAsStringAsync();
                    
                });
                
                return task.Result;
            }

And finally it'll be called in an click event for a button:

private async void ManualSendBtn_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            // Here "client" is object of my class that have been initilized in the main WPF function
            await client.sendReqAsync().ContinueWith((result) =>
            {
                var parsedResponse = JObject.Parse(result.Result);

                this.Dispatcher.Invoke(() =>
                {
                    ordersListView.Items.Add(new OrderDetailsItem
                    {
                        IsSuccessful = bool.Parse(parsedResponse["IsSuccessfull"].ToString()),
                        ServerMessage = parsedResponse["MessageDesc"].ToString(),
                        RequestTime = client.requestTime.ToString("hh:mm:ss.fff"),
                        ResponseTime = client.responseTime.ToString("hh:mm:ss.fff"),
                        ServerLatency = (client.responseTime - client.requestTime).Milliseconds.ToString()
                    });

                });

            });
        }

But it works just for the first click (event occurs) and for next click it gives me:

Inner Exception 1: AggregateException: One or more errors occurred.

Inner Exception 2: AggregateException: One or more errors occurred.

Inner Exception 3: ObjectDisposedException: Cannot access a disposed object.


Edit: Below is the edited code as suggested, but the error is not fixed.

MyHTTPClient

public class MyHTTPClient
    {
        private static readonly HttpClient client = new HttpClient();
        

        StringContent content;
        public DateTime requestTime;
        public DateTime responseTime;

        public void configureReq(string oauthToken)
        {
            var postBodyJSONObject = new
            {
                body = "value"
            };

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", oauthToken);

            content = new StringContent(JsonConvert.SerializeObject(postBodyJSONObject), Encoding.UTF8, "application/json");
        }


        public async Task<string> sendReqAsync()
        {
            requestTime = DateTime.Now;
            var result = await client.PostAsync("https://example.com", content);
            responseTime = DateTime.Now;

            return await result.Content.ReadAsStringAsync();
        }


    }

Also here is my window initializer function:

MyHTTPClient client; // Somewhere in the class
public MainWindow() {
  client = new MyHTTPClient();
  client.configureReq(AUTHORIZATION);
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Sina
  • 53
  • 6
  • 5
    You are using `async/await` wrong. You shouldn't call `ContinueWith` at all. Delete all `ContinueWith` calls and replace it with proper `async/await` code. Like this: ``` requestTime = DateTime.Now; var result = await client.PostAsync("https://api.example.com", content); var responseTime = DateTime.Now; return result.Content.ReadAsStringAsync(); ``` – Neistow Jul 19 '21 at 07:31
  • 2
    Don't mix `await` with `ContinueWith`. Pretty sure `PostAsync` is going to complete first with `result.Result.Content.ReadAsStringAsync` running 2nd hence the error. Continuations can be evil when it comes to error handling –  Jul 19 '21 at 07:33
  • 2
    Also, get out of the habit of calling `task.Result`. Generally it's best to use (in your case) `return await client.PostAsync` (thus omitting the need for `var task = ...` ) –  Jul 19 '21 at 07:34
  • @Neistow but I need to track the time of send and receive for my `POST` request. Have another good idea to achive this? And could you please describe in more details? Why I'm using async/await wrong? How to to write it? – Sina Jul 19 '21 at 07:34
  • @Sina I've edited my comment, check it out – Neistow Jul 19 '21 at 07:36
  • 2
    _[Difference between await and ContinueWith](https://stackoverflow.com/a/18982576/585968)_ –  Jul 19 '21 at 07:40
  • @Neistow Thanks for your reply. I edited the code like you said. Now it gaves me the same `ObjectDisposedException` error on 2nd call of `var x = await client.sendReqAsync();`, there is in my button Click Event. – Sina Jul 19 '21 at 07:51
  • @Sina can you show how you HttpClient is inialized? – Neistow Jul 19 '21 at 07:54
  • @Neistow It's initialized `private static readonly HttpClient client = new HttpClient();` in `MyHTTPClient` class. – Sina Jul 19 '21 at 07:56
  • @Sina well, my guess is somewhere in application you dispose your `HttpClient`. It's really hard to tell what exactly is wrong without full code – Neistow Jul 19 '21 at 08:01
  • @Neistow I've edited the question and inserted the code for `MyHTTPClient` class and where it is init, Also the only call for `sendReqAsync()` is in a Button event handler (in an async way) which uses the `client` object to call... – Sina Jul 19 '21 at 08:14
  • @Sina there are two `sendReqAsync()` implementations in your question. Are both part of the question, or the second is intended as an answer? In the second case, instead of editing your question it is recommended to post a [separate answer](https://stackoverflow.com/help/self-answer). – Theodor Zoulias Jul 19 '21 at 08:38
  • @TheodorZoulias The second one is the edited code as suggested, but as there it couldn't fix the error, I repost it to be checked again... – Sina Jul 19 '21 at 08:54
  • @Sina OK. I edited the question a bit, to clarify this point. – Theodor Zoulias Jul 19 '21 at 13:09

2 Answers2

2

First of all: You shouldn't mix different asynchronous models of programming. In your code you are trying to combine async/await with legacy ContinueWith approach.

You can read more about async/await approach here and about async/await and ContinueWith differences here (Thanks to MickyD for leaving link to this in the comments).

So to cut a long story short your code should look like this:

public async Task<string> SendRequestAsync()
{
    requestTime = DateTime.Now;
    var response = await client.PostAsync("api.example.com", content); 
    var responseTime = DateTime.Now; 
    return await response.Content.ReadAsStringAsync();
}

Note that now code looks much more like synchronous and is a way readable than callbacks with ContinueWith.

You can find more about asynchronous programming in C# on MSDN. I suggest you to read and try to exectue some code in console app before writing it in yourmain app.

Neistow
  • 806
  • 1
  • 8
  • 20
2

In addition to fixing the SendRequestAsync method, as shown by Neistow's answer, you must fix the button's click handler too:

private async void ManualSendBtn_PreviewMouseLeftButtonUp(object sender,
    MouseButtonEventArgs e)
{
    string response = await client.SendRequestAsync();
    var parsedResponse = JObject.Parse(response);
    ordersListView.Items.Add(new OrderDetailsItem
    {
        IsSuccessful = bool.Parse(parsedResponse["IsSuccessfull"].ToString()),
        ServerMessage = parsedResponse["MessageDesc"].ToString(),
        RequestTime = client.RequestTime.ToString("hh:mm:ss.fff"),
        ResponseTime = client.ResponseTime.ToString("hh:mm:ss.fff"),
        ServerLatency = (client.ResponseTime - client.RequestTime).Milliseconds.ToString()
    });
}

There is no reason to mess with the Dispatcher.Invoke. The code after the await will continue running on the UI thread, as it happens by default in all Windows Forms and WPF applications.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
  • You're right, But actually the error wasn't for that. `MyHTTPClient` had a `content` variable which init it's value on the first window load. I change it's place to be in my `sendReqAsync()` function and it works now! Though I don't know why is the reason! – Sina Jul 20 '21 at 11:02