Creating HttpClient (C#) Instances With IHttpClientFactory

Photo of author
Simon Mclellan

Writer, developer.

Whether you are an experienced .NET developer or just starting with C#, this article will give you a few useful (hopefully) tips that you can keep in mind when consuming APIs with HttpClient and IHttpClientFactory.

The IHttpClientFactory is a relatively new thing in the .NET Core world (available since .NET Core 2.1) and yet isn’t used very much (at least not as much as it should be).

Recently, I decided to refresh my knowledge about the HttpClient, and at the same time to learn about the IHttpClientFactory – why it’s here and what problems it solves.

[wpsm_toplist]

What is HttpClient?

HttpClient and IHttpClientFactory are primarily used for consuming RESTful APIs. In essence, HttpClient allows you to quickly create a Request message and send it to the API endpoint. As a result, you will receive a response that may or may not contain the data that you need.

By sending the request to the API endpoint, you can either request for some data from the server, or you can instruct the API server to do some action instead, for example, create/update/delete resources.

Properly implemented APIs usually give you responses containing relevant HTTP Status Codes, indicating what has happened on the server (or not happened). Next, based on the HTTP status code you receive with the response, you can make further decisions.

HttpClient will help you with sending these requests and receiving responses, and the IHttpClientFactory will help you with using the HttpClient correctly. With this, in turn, you will reduce the risk of facing a couple of issues and headaches.

What is IHttpClientFactory?

As you may have already guessed, the IHttpClientFactory is used for creating the HttpClient instances. IHttpClientFactory solves two main problems for us:

[wpsm_list type=”arrow”]

  • Socket exhaustion. This will be demonstrated in this article.
  • The issue with the DNS changes. You can read more about the issue here.

[/wpsm_list]

Here’s what Microsoft says in the documentation:

While this class implements IDisposable, declaring and instantiating it within a using statement is not preferred because when the HttpClient object gets disposed of, the underlying socket is not immediately released, which can lead to a socket exhaustion problem.

So let’s have a look at the example of this issue

using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace ConsoleHttpClient
{
    public class Program
    {
        private static async Task Main()
        {
            Console.WriteLine("Starting connections");

            for (int i = 0; i < 10; i++)
            {
                using (var client = new HttpClient())
                {
                    var result = await client.GetAsync("https://iqunlock.com");

                    Console.WriteLine(result.StatusCode);
                }
            }

            Console.WriteLine("Connections done");
        }
    }
}

So if you run this code, you will see something similar to this

Httpclient

now, if you execute the netstat command (in the elevated mode), you’ll see there are 10 open sockets that can accept data. That can lead to the socket exhaustion, so be sure you never instantiate the HttpClient this way. Instead, you should use the IHttpClientFactory.

Netstat

You will see what is the correct way to initialize the HttpClient in the later examples of this article.

How to set up the HttpClient: Traditional Way

The first, traditional way, would be to initialize a private static HttpClient field in the class, which can be re-used by all methods within that class.

Then, you can make HTTP requests using either the shortcut methods, e.g. GetAsync(), PostAsync(), DeleteAsync(), or PatchAsync().

Or, you can create the HttpRequestMessage object and pass it to the SendAsync() method instead. Both ways are good. Here’s an example below.

        private static HttpClient _httpClient = new HttpClient();

        // Constructor
        public TraditionalHttpClientService()
        {
            // set up HttpClient instance
            _httpClient.BaseAddress = new Uri("http://localhost:57863");
            _httpClient.Timeout = new TimeSpan(0, 0, 30);
        }

        public async Task GetResourceShortcut()
        {
            // Call API
            var response = await _httpClient.GetAsync("api/movies");

            // Ensure we have a Success Status Code
            response.EnsureSuccessStatusCode();

            // Read Response Content (this will usually be JSON content)
            var content = await response.Content.ReadAsStringAsync();

            // Deserialize the JSON into the C# List<Movie> object
            var movies = JsonConvert.DeserializeObject<List<Movie>>(content);
        }

        public async Task GetResourceThroughHttpRequestMessage()
        {
            // Initialize the HttpRequestMessage
            var request = new HttpRequestMessage(HttpMethod.Get, "api/movies");
            
            // Manually add header information
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            // Pass the Request to SendAsync() method
            var response = await _httpClient.SendAsync(request);

            // Ensure we have a Success Status Code
            response.EnsureSuccessStatusCode();

            // Read Response Content (this will usually be JSON content)
            var content = await response.Content.ReadAsStringAsync();

            // Deserialize the JSON into the C# List<Movie> object
            var movies = JsonConvert.DeserializeObject<List<Movie>>(content);
        }

The traditional way of creating HttpClients is fine, but not always. Many times we may have to send requests to many different APIs using different sets of request headers.

How to set up the HttpClient: Named Client

In this case, we have the Named instances of the HttpClient.

Creating them is easy. First, you need to put some settings in the Startup.cs class or wherever you have the IoC containers configured in.

        services.AddHttpClient("MoviesClient", client =>
        {
            client.BaseAddress = new Uri("http://localhost:57863");
            client.Timeout = new TimeSpan(0, 0, 30);
            client.DefaultRequestHeaders.Clear();
        });

You can then create the HttpClient instance with the settings you’ve specified above using the HttpClientFactory as a dependency from the code.

        private readonly IHttpClientFactory _httpClientFactory;

        // Constructor
        public NamedHttpClientService(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        private async Task GetMoviesWithNamedHttpClientFromFactory()
        {
            // Create HttpClient using its name
            var httpClient = _httpClientFactory.CreateClient("MoviesClient");

            // Create Request Message
            var request = new HttpRequestMessage(HttpMethod.Get, "api/movies");
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));

            using (var response = await httpClient.SendAsync(
                request, HttpCompletionOption.ResponseHeadersRead))
            {
                // Ensure we have a Success Status Code
                response.EnsureSuccessStatusCode();

                // Read Response Content (this will usually be JSON content)
                var content = await response.Content.ReadAsStringAsync();

                // Deserialize the JSON into the C# List<Movie> object
                var movies = JsonConvert.DeserializeObject<List<Movie>>(content);
            }
        }

Instead of hardcoding the name of the client, in this case, MoviesClient, you can persist the client name in the settings or in the constant at least.

How to set up the HttpClient: Typed Client

The third way you can use to create HttpClient is called Typed Instance.

First thing you need to do is to create a MoviesClient class, which will have the HttpClient configuration and a helper method to call the API with.

    public class MoviesClient
    {
        private readonly HttpClient _client;

        // Constructor
        public MoviesClient(HttpClient client)
        {
            _client = client;
            _client.BaseAddress = new Uri("http://localhost:57863");
            _client.Timeout = new TimeSpan(0, 0, 30);
            _client.DefaultRequestHeaders.Clear();
        }

        public async Task<IEnumerable<Movie>> GetMovies()
        {
            // Set the request message
            var request = new HttpRequestMessage(HttpMethod.Get, "api/movies");
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            // Call API and get the response
            using (var response = await _client.SendAsync(request))
            {
                // Ensure we have a Success Status Code
                response.EnsureSuccessStatusCode();

                // Read Response Content (this will usually be JSON content)
                var content = await response.Content.ReadAsStringAsync();

                // Deserialize the JSON into the C# List<Movie> object and return
                return JsonConvert.DeserializeObject<List<Movie>>(content);
            }
        }
    }

Add the following to the Startup class. This will add MoviesClient to the IoC container with Transient scope.

services.AddHttpClient<MoviesClient>();

Now you can inject MoviesClient into your services and use it:

var movies = await _moviesClient.GetMovies();

Conclusion

This article was a quick tip kind of article that hopefully provided you with some small but useful tips on how you can (and you should !) create HttpClient instances in C# (.NET Core) using the IHttpClientFactory. I prefer the third way of creating HttpClient instances, and I have four different versions of HttpClient nicely sitting in one of my projects. Wish I’d be required to create even more!