5

I'm working with ASP.NET core 3.1 and I'm writing a web api, starting from the Visual Studio 2019 built-in ASP.NET core web api template.

One of my services has a dependency on the IHttpClientFactory service. I'm using the named client consumption pattern. So, basically, I have code like this:

var client = _httpClientFactory.CreateClient("my-client-name");

I have noticed that the previous method call works even when using the name of a not existing HTTP client. By not existing HTTP client I mean a named HTTP client which has never been defined inside of the Startup.ConfigureServices method.

Put another way, I would expect the following code to throw, but actually it doesn't:

// code in Startup.ConfigureServices
services.AddHttpClient("my-client-name", c =>
{
  c.DefaultRequestHeaders.Add("User-Agent", "UserAgentValue");
});

// code in a custom service. I would expect this line of code to throw
var client = _httpClientFactory.CreateClient("not-existing-client");

Is it possible to configure an ASP.NET core 3.1 application so that the IHttpClientFactory has a strict behavior and code like the previous one throws an exception stating that the requested named client is not defined ?

Enrico Massone
  • 6,464
  • 1
  • 28
  • 56

1 Answers1

3

Is it possible to configure an ASP.NET core 3.1 application so that the IHttpClientFactory has a strict behavior and code like the previous one throws an exception stating that the requested named client is not defined?

Based on the source code for DefaultHttpClientFactory.Create

public HttpClient CreateClient(string name)
{
    if (name == null)
    {
        throw new ArgumentNullException(nameof(name));
    }

    HttpMessageHandler handler = CreateHandler(name);
    var client = new HttpClient(handler, disposeHandler: false);

    HttpClientFactoryOptions options = _optionsMonitor.Get(name);
    for (int i = 0; i < options.HttpClientActions.Count; i++)
    {
        options.HttpClientActions[i](client);
    }

    return client;
}

public HttpMessageHandler CreateHandler(string name)
{
    if (name == null)
    {
        throw new ArgumentNullException(nameof(name));
    }

    ActiveHandlerTrackingEntry entry = _activeHandlers.GetOrAdd(name, _entryFactory).Value;

    StartHandlerEntryTimer(entry);

    return entry.Handler;
}

What you describe is by design. If the client name does not exist a handler will just be added for the name used.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • 1
    thanks for sharing the source. So it seems there is no way to make the factory strict, in the sense I described. I would have preferred a different behavior, so that the developer is able to spot an incorrect usage (requesting a client not explicitly configured). It seems to me a kind of silent failure. – Enrico Massone Aug 06 '21 at 19:05
  • I agree.. just wrote a unit test for this expectation and I can't make it pass since a client is returned always. going to keep digging – Poat Nov 16 '22 at 15:26