.NET Core 2.1 has introduced the HttpClientFactory. This factory allows you to create HttpClient instances to be used in your application.

The HttpClientFactory provides a central location for naming and configuring HttpClients that you are able to pre-configure allowing you to access specific services.

You are able to implement polly-based middleware that takes advantage of polly’s policies to help with resilience.
The HttpClientFactory also gives you the option to manage the lifetime of the HttpClientMessageHandlers to avoid issues that occur when managing these lifetimes with the HttpClients yourself.

What problem does the HttpClientFactory solve?

Before the introduction of the HttpClientFactory in .NET Core 2.1, it was common to use the HttpClient to make HTTP requests to services.  One of the big problems with using the HttpClient was the misuse of it. HttpClient implements IDisposable, when anything implements IDisposable, best practice tells us that we should wrap the calls we are making inside a using statement to allow proper disposal of the object. However the HTTPClient is different, even though it implements IDisposable, we shouldn’t be wrapping this in a using statement. The HttpClient is reusable and thread safe, so it makes it very inefficient and unnecessary to dispose of the HttpClient after each request is made. When you dispose of the HttpClient object the underlying socket is not immediately released. This can cause some serious issues like ‘sockets exhaustion’. The recommended way is to instantiated it once and reused it throughout the life of an application.

To explain this issue in more detail have a read of

“YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE” https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Unfortunately, not disposing of our HttpClient instance does not fix all of the issues with the HttpClient. The issue with creating a single instance of the HttpClient is that it won’t respect DNS changes, because we are creating a single instance of the HttpClient, we are keeping the connection open, ready to be reused.
Due to these issues, the HttpClientFactory was created.

HttpClientFactory Usages

We are going to look at three different ways we can implement and use the HttpClientFactory inside our .NET Core application.

  • Use HttpClientFactory Directly
  • Named HttpClient Factories
  • Typed HttpClient Factories

No matter what method you choose to implement the HttpClientFactory you will need to register the AddHttpClient inside the ConfigureServices method in the startup class.

Ensure that you have the Microsoft.Extensions.Http package installed. Then register it by saying

services.AddHttpClient();

.NET Core Application Template Setup

Let’s first have a look at what our application setup looks like before we get into implementing the HttpClientFactory.

We will be creating a .NET Core application that contains an ActivityController and a model class named Board.

The API that we will be calling off to, to demonstrate using the HttpClientFactory is the Bored API, you can read more about this API here https://www.boredapi.com/

Below is an image that shows what our solution setup looks like.

2018-12-01_6-58-56

Inside our Startup class, we install the Microsoft.Extensions.Http package and register the AddHttpClient() method inside our ConfigureServices method inside our startup class.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddHttpClient();
}

Inside our ActivityController we create an empty GET method GetRandomActivity and add the route Activity/Random

public class ActivityController
{
   [HttpGet]
   [Route("Activity/Random")]
   public async Task<Board> GetRandomActivity()
   {
   }
}

Use HttpClientFactory Directly

Let’s have a look at how we are able to use the HttpClientFactory directly inside our application.

public class ActivityController
{
    private readonly IHttpClientFactory _httpClientFactory;

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

    [HttpGet]
    [Route("Activity/Random")]
    public async Task<Board> GetRandomActivity()
    {
        var client = _httpClientFactory.CreateClient();

        var response = await client.GetStringAsync("http://www.boredapi.com/api/activity/");

        return JsonConvert.DeserializeObject<Board>(response);
    }
}

We have dependency injected the IHttpClientFactory into our controller, inside our GetRandomActivity we create a client then call off using the GetStringAsync method to our boardapi. Finally we deserialize the json object that we are getting back into our Board model.

Named HttpClient Factories

The second approach we can take in implementing the HttpClientFactory is a named HttpClient factory. Here we are able to name our HttpClient and set a base address when registering the HttpClient in the ConfigureServices method in our startup class.

Let’s have a look at how we register a named HttpClient.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddHttpClient("boardApi", c =>
    {
        c.BaseAddress = new Uri("http://www.boredapi.com/api/");
        c.DefaultRequestHeaders.Add("Accept", "application/json");
    });
}

In our controller class, we can then use our named HttpClient by creating a client and passing in the registered name that we set in our ConfigureServices method.

[Route("Activity")]
public class ActivityController
{
    private readonly IHttpClientFactory _httpClientFactory;

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

    [HttpGet]
    [Route("Random")]
    public async Task<Board> GetRandomActivity()
    {
        var client = _httpClientFactory.CreateClient("boardApi");

        var response = await client.GetStringAsync("activity");

        return JsonConvert.DeserializeObject<Board>(response);
    }
}

Typed HttpClient Factories

Our third way of implementing the HttpClientFactory is the Typed HttpClientFactory. We will first create a BoardApiClient class. We create a HttpClient property, then we add the HttpClient parameter into our constructor

public class BoardApiClient
{
    public HttpClient Client { get; }

    public BoardApiClient(HttpClient httpClient)
    {
    }
}

Inside our constructor we want to add our base address and any required headers and set it to our HttpClient property that we have created above.

public BoardApiClient(HttpClient httpClient)
{
    httpClient.BaseAddress = new Uri("http://www.boredapi.com/api/");
    httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
    Client = httpClient;
}

Back inside our startup class, we can now register our Typed HttpClient Factory like so

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddHttpClient<BoardApiClient>();
}

In our controller class we can use our Typed HttpClient Factory by dependency injecting it into our ActivityController

public class ActivityController
{
    private readonly BoardApiClient _boardApiClient;

    public ActivityController(BoardApiClient boardApiClient)
    {
        _boardApiClient = boardApiClient;
    }

Then simply call _boardApiClient.Client.GetStringAsync("activity");

[HttpGet]
[Route("Activity/Random")]
public async Task<Board> GetRandomActivity()
{
    var response = await _boardApiClient.Client.GetStringAsync("activity");

    return JsonConvert.DeserializeObject<Board>(response);
}

While this works fine, the way I like to do it is to encapsulate the HttpClient and calls that we are making, this will also allow you to more easily unit test your application.

If we change our HttpClient property that we created to a private field

private readonly HttpClient _client;

Then create a method inside our BoardApiClient class, that makes the Http call.

public async Task<Board> GetRandomActivity()
{
    var response = await _client.GetStringAsync("activity");

    return JsonConvert.DeserializeObject<Board>(response);
}

We also want to create a IBoardApiClient interface and add our method

public interface IBoardApiClient
{
    Task<Board> GetRandomActivity();
}

Make sure you implement the interface to our BoardApiClient class.

This is what our BoardApiClient class now looks like

public class BoardApiClient : IBoardApiClient
{
    private readonly HttpClient _client;

    public BoardApiClient(HttpClient httpClient)
    {
        httpClient.BaseAddress = new Uri("http://www.boredapi.com/api/");
        httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
        _client = httpClient;
    }
    
    public async Task<Board> GetRandomActivity()
    {
        var response = await _client.GetStringAsync("activity");

        return JsonConvert.DeserializeObject<Board>(response);
    }
}

We want to register our IBoardApiClient inside our ConfigureServices class

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddHttpClient<IBoardApiClient, BoardApiClient>();
}

Then we can simply call off to our newly created method in our ActivityController

return await _boardApiClient.GetRandomActivity();

This is what our ActivityController now looks like

public class ActivityController
{
    private readonly IBoardApiClient _boardApiClient;

    public ActivityController(IBoardApiClient boardApiClient)
    {
        _boardApiClient = boardApiClient;
    }

    [HttpGet]
    [Route("Activity/Random")]
    public async Task<Board> GetRandomActivity()
    {
        return await _boardApiClient.GetRandomActivity();
    }
}

HttpClient Lifetimes

By default the HttpClient is set to expire after 2 mins. There are times we might want to override this value. We can do this by setting the SetHandlerLifetime when registering our HttpClient in our ConfigureServices method

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddHttpClient<IBoardApiClient, BoardApiClient>()
        .SetHandlerLifetime(TimeSpan.FromMinutes(10));
}

Summary

We have looked at three ways we can implement and use the HttpClientFactory, by using the HttpClientFactory directly in our application, using a named HttpClientFactory and a Typed HttpClientFactory.

Using the HttpClientFactory removes the issues that we can run into when using the HttpClient, such as ‘sockets exhaustion’, due to not re-using the HttpClient throughout the life of an application.

When reusing your HttpClient you could then run into the issue of the client not respecting DNS changes. While the HttpClientFactory by default has a handler lifetime set to 2 mins, to get around DNS change issue, we are also able to set this handler lifetime to whatever we want.