Peter
Peter

Reputation: 7804

HttpClient - PostAsync doesn’t return

Does anyone know why HttpClient - PostAsync doesn’t return. It just does nothing. I have had it working occasionally especially for one off posts but it seems sometimes to not do anything especially under load and it doesn't throw an exception which of course makes my code unreliable and hard to debug.

I have tried adding ConfigureAwait(false) It makes not difference.

I suspect the Task is failing to 'pack'

This is in a core 3.0 console app run on macOS Catalina using visual studio code

This code is pretty much copied from Microsoft's documentation and I’m calling Microsoft Graph when posting.

public static async Task PostAsync(HttpClient httpClient, string url, string token, HttpContent content, Action<JObject> processResult, ILogger log)
    {
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        // content.Headers.Clear();
        content.Headers.Add("Content-Type", "application/json");
        try
        {
            HttpResponseMessage response = await httpClient.PostAsync(url, content);
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var result = JsonConvert.DeserializeObject(json) as JObject;
                processResult(result);
            }
            else
            {
                var errorContent = await response.Content.ReadAsStringAsync();
                log.LogError(errorContent);
            }
        }
        catch (System.Exception ex)
        {
            log.LogError(ex, ex.Message);
            throw;
        }
    }

Here is an example of the calling code

public async Task SendInvitation(string token, Invitation invitation, ILogger logger)
{
    var stringContent = new StringContent(JsonConvert.SerializeObject(invitation), Encoding.UTF8, "application/json");
    await HttpHelpers.PostAsync(
        Client,
        "https://graph.microsoft.com/v1.0/invitations",
        token,
        stringContent,
        result => logger.LogInformation(DebugHelpers.Print(result)),
        logger);
}

Answered (Sort of)

If I change

HttpResponseMessage response = await httpClient.PostAsync(url, content);

to

HttpResponseMessage response = httpClient.PostAsync(url, content).GetAwaiter().GetResult();

It seems to work but it's slow because what I'm doing is using blocking code. I think this is a quirk of core 3 on macOS. I don't like that this is happening.

More Info

I'm doing a lot of looping.

It seems that if I put all the things I'm awaiting in a taskList it behaves properly.

\\ Pseudo Code
var taskList = new List<Task>();

foreach(var thing in things){
  taskList.Add(HttpHelpers.PostAsync(...things));
}

await Task.WhenAll(taskList);

Upvotes: 2

Views: 3671

Answers (3)

smirad91
smirad91

Reputation: 17

You can wait for HttpResponseMessage certain condition. Inspect HttpResponseMessage in debug, with 2 given steps. Screenshot of debug process: postAsync debug

  1. return HttpResponseMessage from method with postAsync:

     private static async Task<HttpResponseMessage> methodWithPostAsync(){
        ...
        response = await client.PostAsync(url, data);
        return response
        }
    
  2. call method and wait for response message status:

    Task<HttpResponseMessage> content= methodWithPostAsync();
    while (!content.IsCompleted)
    {
        Console.WriteLine("busy");
        System.Threading.Thread.Sleep(1000);
    }
    

Upvotes: 0

Spring Hgui
Spring Hgui

Reputation: 1

try ConfigureAwait like this

HttpResponseMessage response = await httpClient.PostAsync(url, content).ConfigureAwait(false);

Upvotes: -2

tubakaya
tubakaya

Reputation: 497

Please check whether all calls you make in the code execution path support asynchronousity. For example once I spent quite some time figuring out a nuget package called CommandLineParser did not support async calls. I was using it like so :

public static void Main(string[] args) 
{
     Parser.Default.ParseArguments<Options>(args) 
                   .WithParsed(async options => 
                    { await httphelper.PostAsync(...); 
                    } 
}

I fixed the issue by changing it to something like

public static void Main(string[] args) 
{
     Parser.Default.ParseArguments<Options>(args) 
                   .WithParsed(options => 
                    { httphelper.PostAsync(...).Result; 
                    } 
}

So please check you are not using some calls that do not support async in the way.

Upvotes: 5

Related Questions