-1

I am doing Async and Await with C# ASP.Net MVC, with Target .Net Framework 4.8 using Visual Studio 2022. So no matter what, I always get an exception when I call httpClient.PostAsync(...). I can call httpClient.PostAsync(...).ConfigureAwait(false) which doesn't give me an immediate exception and my code after proceeds normally, but I still get an exception by the last closing bracket of my method.

So first, I am making httpClient a private static variable in my class, and I think this is the recommended practice:

private static readonly HttpClient httpClient = new HttpClient();

Here is the code where I call httpClient.PostAsync(...) but I don't call ConfigureAwait(false) and this gives me an immediate exception in the Visual Studio 2022 debugger and I never make it to the end curly bracket of my method (I simplified the code a little):

private async Task MyMethod(string json, Dictionary<string, string> headerDictionary)
{
  string accessToken = GetAccessToken();


  string webhookID = WebConfigurationManager.AppSettings["PayPalWebhookID"];
  var paypalVerifyRequestJsonString = ... ...;

  string payPalServHref = WebConfigurationManager.AppSettings["PayPalV2ServHref"];

  httpClient.DefaultRequestHeaders.Clear();
  httpClient.DefaultRequestHeaders.Add("authorization", "Bearer " + accessToken);     
  var content = new StringContent(paypalVerifyRequestJsonString, Encoding.UTF8, "application/json");

  // if I return here, there is no exception
  //return;

  var resultResponse = await httpClient.PostAsync(payPalServHref + "/v1/notifications/verify-webhook-signature", content);

  // the end curly bracket of this method (the } below) is never reached
}

An exception is immediately thrown when I call httpClient.PostAsync(...):

enter image description here

Now I just change httpClient.PostAsync(...) to httpClient.PostAsync(...).ConfigureAwait(false) and my code proceeds normally when I debug it, but an exception is always thrown by the end curly bracket of my method (again, I simplified the code a little):

private async Task MyMethod(string json, Dictionary<string, string> headerDictionary)
{
  string accessToken = GetAccessToken();


  string webhookID = WebConfigurationManager.AppSettings["PayPalWebhookID"];
  var paypalVerifyRequestJsonString = ... ...;

  string payPalServHref = WebConfigurationManager.AppSettings["PayPalV2ServHref"];

  httpClient.DefaultRequestHeaders.Clear();
  httpClient.DefaultRequestHeaders.Add("authorization", "Bearer " + accessToken);     
  var content = new StringContent(paypalVerifyRequestJsonString, Encoding.UTF8, "application/json");

  // if I return here, there is no exception
  //return;

  var resultResponse = await httpClient.PostAsync(payPalServHref + "/v1/notifications/verify-webhook-signature", content).ConfigureAwait(false);

  // lines after httpClient.PostAsync(...).ConfigureAwait(false); are reached and I can debug it, but there will always be an exception 
  // thrown by the end curly bracket below (the } below) 

  // if I undo this comment for return, the return statement is called but an exception is still thrown by the end curly bracket below (the } below)
  // return;

}

And as I said, ConfigureAwait(false) allows my code to proceed normally, but an exception is always thrown by the end curly bracket of my method:

enter image description here

I am calling MyMethod(...) as follows (actually, PayPal Sandbox is calling my C# ASP.Net Webhook method and inside my Webhook method, the call is made to await MyMethod(...):

await MyMethod(json, headerDictionary);

The interesting thing is, if I remove all instances of Async and Await, and I forcefully call httpClient.PostAsync(...) synchronously, that is I call httpClient.PostAsync(...).Result, everything works and I don't get any exceptions at all:

var resultResponse = httpClient.PostAsync(payPalServHref + "/v1/notifications/verify-webhook-signature", content).Result;

Does anybody know why I keep getting exceptions when using Async and Await when calling httpClient.PostAsync(...) or calling httpClient.PostAsync(...).ConfigureAwait(false)?

Edit: Some people requested the function that is calling MyMethod(...). So here is the function that is calling MyMethod(...). It's just a Webhook method that PayPal Sandbox calls:

public async Task<ActionResult> WebhookTester()
{
  try
  {
    // get the JSON from PayPal
    Stream req = Request.InputStream;
    req.Seek(0, System.IO.SeekOrigin.Begin);
    string json = new StreamReader(req).ReadToEnd();

    // Get the header from PayPal and store it in a dictionary
    var headers = HttpContext.Request.Headers;
    Dictionary<string, string> headerDictionary = new Dictionary<string, string>();
    var nvc = headers.AllKeys.SelectMany(headers.GetValues, (k, v) => new { key = k, value = v });
    foreach (var header in nvc)
    {
      headerDictionary.Add(header.key, header.value);
    }

    // Verify the JSON and header. If PayPal returns SUCCESS then process the order. If it's FAILURE, don't process the order.
    await MyMethod(json, headerDictionary);

  }
  catch (Exception ex)
  {

  }

  return new HttpStatusCodeResult(200);
}
Programmer Joe
  • 104
  • 1
  • 8
  • Have you seen this https://stackoverflow.com/a/53013207/6530134 ? – Timothy G. Jun 28 '23 at 13:49
  • @TimothyG Yup. I saw that solution earlier. Unfortunately, I tried adding between ... in my web.config. It did not fix the problem, I still get exceptions. – Programmer Joe Jun 28 '23 at 13:52
  • 1
    from where the MyMethod is called? I have a feeling somewhere and await was forgotten, maybe even outside your code. To test my theory instead of await MyMethod(json, headerDictionary); just got with MyMethod(json, headerDictionary).GetAwaiter().GetResult(), keep the await on http client post. – SilentTremor Jun 28 '23 at 13:58
  • or maybe is called from an async method without a task returned – SilentTremor Jun 28 '23 at 14:15
  • @SilentTremor Thanks. I just tried your suggestion of calling GetAwaiter().GetResult(). If I call MyMethod(json, headerDictionary).GetAwaiter().GetResult(), it works and I don't get any exceptions. However, I would think this is just the same as removing all instances of Async and Await, and calling httpClient.PostAsync(...).Result, which also works for me? And I am reading that .GetAwaiter().GetResult() can also lead to deadlocks: https://stackoverflow.com/questions/39007006/is-getawaiter-getresult-safe-for-general-use – Programmer Joe Jun 28 '23 at 14:18
  • nope, that is not a solution, that is an indication there's no problem inside MyMethod. To figure out where the issue is, go on the call chain and review if any call is made like MyMetod(args) without any await or maybe the caller is defined like async void or async something (not an async Task) if you spot that, there's the issue. – SilentTremor Jun 28 '23 at 14:21
  • 1
    Please post the code that comes after the `PostAsync` call and before the bracket. My guess is you are doing something there that is causing this. – JuanR Jun 28 '23 at 14:28
  • @SilentTremor I edited the question and included the method that is calling MyMethod(...). The method that is calling MyMethod(...) is just a Webhook method that PayPal Sandbox is calling. Please check the Webhook method in the edited question above, it's WebhookTester(). – Programmer Joe Jun 28 '23 at 14:29
  • @JuanR I think the code after PostAsync is irrelevant. Even if I remove all code after PostAsync, the end curly bracket of MyMethod(...) still throws an exception. I have also put a return before and after PostAsync. The return before PostAsync works, but when the return after PostAsync is called, the end curly bracket will throw an exception. However, I did update my question to include the method that is calling MyMethod(...). – Programmer Joe Jun 28 '23 at 14:32
  • 1
    Don't assume. Provide the code. You are talking about threading here. Everything is relevant. – JuanR Jun 28 '23 at 14:36
  • By the way, do you really need this to be async? Can't you just use `.Result` and call it a day? – JuanR Jun 28 '23 at 14:40
  • @JuanR The code after PostAsync is return;. That's it, just a return statement. The return statement is executed and the debugger will get past this return statement, but I will still get an exception at the end curly bracket of MyMethod(...). I just tested it right now. – Programmer Joe Jun 28 '23 at 14:45
  • @juanr I think .Result can lead to a deadlock: https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d. Aside from this medium article, I have read other responses on Stackoverflow that say .Result is not safe and can lead to a deadlock in various circumstances. – Programmer Joe Jun 28 '23 at 14:52
  • 1
    Dude, just add the line. If it's just a `return` then why leave it out? What I am interested on is what you are doing with the `resultResponse` variable after the call. Is that what you are returning from the method? – JuanR Jun 28 '23 at 14:54
  • @JuanR I have updated the question to show that even a return statement after PostAsync will cause an exception at the end curly bracket. When I call return, I am not doing anything with the resultResponse variable. – Programmer Joe Jun 28 '23 at 14:59
  • dumb question, how WebhookTester() is called? – SilentTremor Jun 28 '23 at 15:03
  • @SilentTremor It's called by PayPal. It's a Webhook that PayPal calls. You set the Webhook URL at PayPal and PayPal will call your Webhook: https://developer.paypal.com/api/rest/webhooks/. – Programmer Joe Jun 28 '23 at 15:05
  • undestood, just checking if it was called by something in the code, understood – SilentTremor Jun 28 '23 at 15:05
  • is MyContext referenced httpclient in the same class with the static private immutable httpclient? – SilentTremor Jun 28 '23 at 15:31
  • @SilentTremor If I understand your question correctly, yes `private static readonly HttpClient httpClient = new HttpClient();` is in the same class as my MyMethod(...) method. In addition, I tried Bluetonic's suggestion below, where I commented out the `private static readonly HttpClient httpClient`, and used followed Bluetonic's code to try `using (var httpClient = new HttpClient()) {...}` locally. However, even if `using (var httpClient = new HttpClient()) {...}` is used locally instead of `private static readonly HttpClient httpClient`, I still get an exception at the end curly bracket – Programmer Joe Jun 28 '23 at 16:20
  • @SilentTremor I fixed the problem. The solution was to move all my code in `MyMethod(...)` to the parent `WebhookTester()` method. Please see the answer that I have just posted for more details. Thank you for your help. – Programmer Joe Jun 29 '23 at 12:10

2 Answers2

1

I moved all my code from MyMethod(...) to WebhookTester(), including the await httpClient.PostAsync(...).ConfigureAwait(false) call. When I did that, no exception was thrown.

I don't know the exact reason why calling await MyMethod(...) from my WebhookTester() method is causing the exception and why moving all the code to WebhookTester() works all of a sudden and no exceptions are thrown. My theory is that the class I am using that contains both WebhookTester() and MyMethod(...), is inheriting another base class, and some property in my base class is generating a null exception when await MyMethod(...) is finished being called. However, the Visual Studio debugger never shows me the null exception in my base class - my overall experience is that debugging asynchronous code with Visual Studio is pretty hard. Anyways, this is my theory - in the end, I don't know exactly why putting my code in await MyMethod(...) leads to an exception, but taking the same code and putting it in WebhookTester() does not cause an exception. Maybe it's also an issue with nested asynchronous methods where the class is inheriting another base class.

Programmer Joe
  • 104
  • 1
  • 8
  • most likely your code is the problem, the only thing that could throw null exception was the http client, how? it's (was) in your code – SilentTremor Jun 29 '23 at 13:18
  • happy you fixed it – SilentTremor Jun 29 '23 at 13:18
  • @SilentTremor Yes, I think my code is the problem. But the only thing I can think of is the base class, that the class holding `WebhookTester()` and `MyMethod(...)` inherited from. It might be related to the threading, and either there is a null exception in the base class when `MyMethod(...)` is finished executing, or some other resource in the base class isn't disposed of. If the Visual Studio debugger were better, then I could probably pinpoint the issue. But the Visual Studio debugger isn't so great when debugging asynchronous code. Oh well, at least I got it working. – Programmer Joe Jun 29 '23 at 17:49
0

I can't comment yet, so apologies for writing an answer.

This might be happening because you are using HttpClient as a static variable. Having said that, just reading Microsoft's site, it suggests it is the correct way. If somewhere in the code HttpClient is being disposed, it could lead to an exception. Check if HttpClient is not disposed somewhere in the code.

Try this with the using statement:

private async Task MyMethod(string json, Dictionary<string, string> headerDictionary)
{
    using (var httpClient = new HttpClient())
    {
        string accessToken = GetAccessToken();
        string webhookID = WebConfigurationManager.AppSettings["PayPalWebhookID"];
        var paypalVerifyRequestJsonString = ... ...;
        string payPalServHref = WebConfigurationManager.AppSettings["PayPalV2ServHref"];

        httpClient.DefaultRequestHeaders.Clear();
        httpClient.DefaultRequestHeaders.Add("authorization", "Bearer " + accessToken);
        var content = new StringContent(paypalVerifyRequestJsonString, Encoding.UTF8, "application/json");

        var resultResponse = await httpClient.PostAsync(payPalServHref + "/v1/notifications/verify-webhook-signature", content).ConfigureAwait(false);

        //...
    }
}

This helps prevent potential issues related to reusing the same HttpClient instance across multiple requests. Hope it helps

Bluetonic
  • 53
  • 5
  • Hi. I just tried using (var httpClient = new HttpClient()){ ... } and I commented out the private static readonly HttpClient httpClient = new HttpClient();. I still get an exception by the end closing curly bracket of my method. Also, I am not using httpClient anywhere else. – Programmer Joe Jun 28 '23 at 14:08
  • 1
    Hi @Bluetonic. I fixed the problem. The solution was to move all my code in `MyMethod(...)` to the parent `WebhookTester()` method. Please see the answer that I have just posted for more details. Thank you for your help. – Programmer Joe Jun 29 '23 at 12:11
  • Happy you fixed it. I've noted that in the black book for future reference when I undoubtedly have that issue :) – Bluetonic Jun 29 '23 at 14:06