-1

Introduction

I've been working in ASP.NET Web API where I bumped into some weird interactions when trying to call another API. I've tried 3 different ways to use HttpClient, all with different results.
Have tested everything using Postman, hence some results.

1. Sync HttpClient call

private static string GetAPI(string url)
{
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri(url);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.Add("x-api-key", "SomeSecretApiKey");

        HttpResponseMessage response = client.GetAsync(url).Result;
        string contents = response.Content.ReadAsStringAsync().Result;

        if (response.IsSuccessStatusCode)
        {
            return contents;
        }
    }

    return null;
}

Result

Does work, but I want to use async

2. Async httpClient call

private static async Task<string> GetAPI(string url)
{
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri(url);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.Add("x-api-key", "SomeSecretApiKey");

        HttpResponseMessage response = await client.GetAsync(url);
        string contents = await response.Content.ReadAsStringAsync();

        if (response.IsSuccessStatusCode)
        {
            return contents;
        }
    }

    return null;
}

Result

Does not work. Will not go beyond the line HttpResponseMessage response = await client.GetAsync(url);, since there's never a response?

3. Async httpClient call with shared HttpClient

private static readonly HttpClient client = new HttpClient();

private static async Task<string> GetAPI(string url)
{
    client.BaseAddress = new Uri(url);
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    client.DefaultRequestHeaders.Add("x-api-key", "SomeSecretApiKey");

    HttpResponseMessage response = await client.GetAsync(url);
    string contents = await response.Content.ReadAsStringAsync();

    if (response.IsSuccessStatusCode)
    {
        return contents;
    }

    return null;
}

Result

Does work once. Will then throw the error:

System.AggregateException: One or more errors occurred. ---> System.InvalidOperationException: This instance has already started one or more requests. Properties can only be modified before sending the first request.

(as suggested by this SO answer).

Question

I would appreciate it if someone could explain why these results are so different and/or give an alternative way to make a basic HttpClient call (still would like to use async).

10
  • 2
    FYI: Your third example is not a singleton. Its just a shared HttpClient (which is the way you should be using it). Also your third example is not even Async (nor would it compile). You need to return a Task and mark the method as async. Commented May 23, 2018 at 14:36
  • 1
    Example 2 is most likely a common deadlock which occurs when you mix async with synchronous code (ie. not using async all the way). This could be caused by a call to GetAPI but using .Result instead of await. See also releated An async/await example that causes a deadlock. Commented May 23, 2018 at 14:48
  • 1
    @WouterVanherck Well once you make your third example compile, that looks like every bit of HttpClient code I have written. Nothing stands out as "wrong". You have to worry about DNS issues with a shared HttpClient (it wouldnt respect TTL). Commented May 23, 2018 at 14:49
  • 1
    Here's a good article about using the HttpClient: blogs.msdn.microsoft.com/henrikn/2012/08/07/… Commented May 23, 2018 at 14:58
  • 1
    Here is a popular article that I found easy to digest when I started working with async / await. blog.stephencleary.com/2012/07/dont-block-on-async-code.html Commented May 23, 2018 at 14:59

1 Answer 1

0

@Igor has informed me about the deadlock issue in my second example.
So I currently used the HttpClient as such (async):

private static async Task<string> GetAPI(string url)
    {
    // TODO: Use a shared instance of HttpClient
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri(url);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.Add("x-api-key", "SomeSecretApiKey");

        var jsonString = await client.GetStringAsync(url).ConfigureAwait(false);
        return jsonString;
    }
}

While some things have been made clear, the entire question hasn't been answered yet. I therefore won't accept this as an answer, yet like to thank those for providing me good information on the use of HttpClient.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.