Search code examples
c#async-awaitdotnet-httpclientihttpclientfactory

How does IHttpClientFactory.CreateClient(customClient) creates the async definition of an HttpClient?


I've stumbled in a peculiar case where the named HttpClient that I want to build is run with an async function, like this:

  services.AddHttpClient("customClient", async (serviceProvider, httpClient) => {...});

And then when I try to recall it from an instance of IHttpClientFactory inside a service in which I want to use it, like this:

  //Constructor start
  this.httpClient = httpClientFactory.CreateClient("customClient");
  //Constructor end

I end up with a half-assed HttpClient, where the BaseAddress is set but the DefaultRequestHeaders.Authorization is missing despite both are set in same AddHttpClient async function(but the Authorization header is only set after awaiting 1 call).

So I was wondering, like in the question title: what happen when CreateClient(name) is called synchronously meanwhile the HttpClient setup is asynchronous?

Isn't there a mechanism that awaits this kind of definitions? From what I tried, it doesn't seem so.


Solution

  • So I was wondering, like in the question title: what happen when CreateClient(name) is called synchronously meanwhile the HttpClient setup is asynchronous?

    The setup isn't allowed to be asynchronous. If you look at all the overloads, none of them take an asynchronous delegate.

    And if you think about it, that makes sense. Dependency injection in general has rejected asynchronous initialization because if they allowed it, then every dependency resolution would have to be awaited.

    Isn't there a mechanism that awaits this kind of definitions? From what I tried, it doesn't seem so.

    Nope. The common patterns are:

    • If this is a one-time initialization, then do it before setting up your DI, and use the resulting value in your DI setup.
    • If this must be done after startup, then consider injecting an asynchronous HttpClient factory (no type like this is built-in; you'll have to use your own).
    • If this needs to be done periodically (e.g., refreshing access tokens), then what you really want is a middleware (delegating handler) that (re-)acquires the token on demand.