DEV Community

Cover image for C# Tip: use IHttpClientFactory to generate HttpClient instances
Davide Bellone
Davide Bellone

Posted on • Originally published at code4it.dev

C# Tip: use IHttpClientFactory to generate HttpClient instances

The problem with HttpClient

When you create lots of HttpClient instances, you may incur Socket Exhaustion.

This happens because sockets are a finite resource, and they are not released exactly when you 'Dispose' them, but a bit later. So, when you create lots of clients, you may terminate the available sockets.

Even with using statements you may end up with Socket Exhaustion.

class ResourceChecker
{
    public async Task<bool> ResourceExists(string url)
    {
        using (HttpClient client = new HttpClient())
        {
            var response = await client.GetAsync(url);
            return response.IsSuccessStatusCode;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Actually, the real issue lies in the disposal of HttpMessageHandler instances. With simple HttpClient objects, you have no control over them.

Introducing HttpClientFactory

The HttpClientFactory class creates HttpClient instances for you.

class ResourceChecker
{
    private IHttpClientFactory _httpClientFactory;

    public ResourceChecker(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<bool> ResourceExists(string url)
    {
        HttpClient client = _httpClientFactory.CreateClient();
        var response = await client.GetAsync(url);
        return response.IsSuccessStatusCode;
    }
}
Enter fullscreen mode Exit fullscreen mode

The purpose of IHttpClientFactory is to solve that issue with HttpMessageHandler.

An interesting feature of IHttpClientFactory is that you can customize it with some general configurations that will be applied to all the HttpClient instances generated in a certain way. For instance, you can define HTTP Headers, Base URL, and other properties in a single point, and have those properties applied everywhere.

How to add it to .NET Core APIs or Websites

How can you use HttpClientFactory in your .NET projects?

If you have the Startup class, you can simply add an instruction to the ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient();
}
Enter fullscreen mode Exit fullscreen mode

You can find that extension method under the Microsoft.Extensions.DependencyInjection namespace.

Wrapping up

In this article, we've seen why you should not instantiate HttpClients manually, but instead, you should use IHttpClientFactory.

Happy coding!

🐧

Top comments (6)

Collapse
 
fluffynuts profile image
Davyd McColl

Since HttpClient instances are thread-safe and don't hold much in the way of state (except if you're setting up, eg, default headers or base urls), you can also use a singleton pattern with them - this is a performant way to do a lot of concurrent http requests.

Collapse
 
bellonedavide profile image
Davide Bellone

On one hand you're right.

On the other hand, using a single instance of HttpClient creates a bottleneck.

Creating them using HttpClientFactory allows you to use on of the clients currently idle in the connections pool.

Collapse
 
fluffynuts profile image
Davyd McColl

Incorrect - unless you want to set default headers or connection handlers (docs.microsoft.com/en-us/aspnet/co...), HttpClient is thread-safe (docs.microsoft.com/en-us/dotnet/ap...) and does not block on requests.

In fact, we use this static pattern a lot in a highly-concurrent environment, and have been doing so for years.

TL;DR: if you're willing to specify all requests fully (ie, full url, required headers (if any)) then a static HttpClient is just fine. If you aren't doing tricks with headers, then your only requirement here is to fully qualify request urls, which isn't just a big requirement.

Here's a demo to prove it: github.com/fluffynuts/ConcurrentHt... - if you run that, I see 10 sequential requests to google.com taking around 5 seconds, and 10 parallel requests taking around 1 second.

Thread Thread
 
bellonedavide profile image
Davide Bellone

Ah, I see. Interesting.

Silly question: if it works, why did the .NET team created the HttpClientFactory? Only for customizing single HTTP clients with stuff like BaseUrl and headers?

Thread Thread
 
fluffynuts profile image
Davyd McColl

docs.microsoft.com/en-us/dotnet/ar...

Mostly because most people often use HttpClient incorrectly - disposing and recreating clients, which is expensive. Hiding this behind a factory means you don't see that you're getting the same instance, and you have an injectable service to provide this instance. Also, you can create clients with common config (headers, baseurl, etc) and just rely on getting the "correct one" when you ask for it by name or as an injectable constructor parameter for a service of your own. The factory should ensure better life-cycles for your consumed HttpClient instances because they can (as demonstrated) be re-used and used in parallel, without issue.

Thread Thread
 
fluffynuts profile image
Davyd McColl

I must be clear that there's nothing wrong with using IHttpClientFactory - just that it's not the only way to work with HttpClient instances performantly.