DEV Community

Mahesh More
Mahesh More

Posted on

Different ways to implement IHttpClientFactory in .NET core apps

Why not HttpClient?

We have been using the HttpClient class for REST API calls and I am not saying this is bad practice but most of the times developer use it wrongly and it has some limitations which cause serious issues, I have noted a few of them:

  • Even we dispose of instance underlying socket doesn't release immediately, which leads to socket exhaustion.
using(var client = new HttpClient())
{
    //do something with HTTP client
}
Enter fullscreen mode Exit fullscreen mode

Do you think creating an instance using the using block will work? The answer is it won't work. If you are curious about what is the current state or wanted to confirm that the underline socket is closed or not then run the below command and it will show a list of connections that are in a wait or established state.

> netstat -na | find 'Port_Number'
Enter fullscreen mode Exit fullscreen mode
  • To avoid socket exhaustion if you create a singleton or static instance throughout the application lifetime then it's not the ultimate solution because when you reuse the instance which means you are reusing the connection until the socket is closed. So the connection won't get updates from the server, effectively it will fail to handle DNS changes when we switch from staging to production environment. You can find more details here.

  • Creating an instance inside the using directive is not good practice as it tries to dispose of instances of HttpClient along with that it will dispose of the HttpClientHandler as well. Under the hood, HttpClient uses the HttpClientHandler for actual socket connection, but underhood implementation is out of scope for this article but I will create a separate detailed article on it.

To address the above issue .Net core introduced the IHttpClientFactory interface which we can use for HttpClient instance creation using DI.

Different ways to use IHttpClientFactory:

1. Basic usage:

You can register IHttpClientFactory using AddHttpClient extension method.
Program.cs or Startup.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddHttpClient();
Enter fullscreen mode Exit fullscreen mode

TestController.cs: DI will inject the dependency in the constructor.

class TestController : Controller
{
   private readonly IHttpClientFactory  _factory;

   public TestController(IHttpClientFactory factory)
   {
     _factory = factory;
   }

   [HttpGet]
   public async Task<IActionResult> Get()
   {
     var httpClient = _factory.CreateClient();
   }
}
Enter fullscreen mode Exit fullscreen mode

2. Named Clients:

If you need a different configuration for each HttpClient then you can use named client DI and get an instance using the same name. Let's say you need to interact with 2 third-party APIs using HTTP REST calls and both need to be configured differently using the named client as below.

Startup.cs:

builder.Services.AddHttpClient("ClientA", httpClient =>
{
    httpClient.BaseAddress = new Uri("https://apiA.com/");
    //ToDo more configuration
});

builder.Services.AddHttpClient("ClientB", httpClient =>
{
    httpClient.BaseAddress = new Uri("https://apiB.com/");
    //ToDo more configuration
});
Enter fullscreen mode Exit fullscreen mode

TestController.cs:

var httpClientA = _factory.CreateClient("ClientA");

var httpClientB = _factory.CreateClient("ClientB");
Enter fullscreen mode Exit fullscreen mode

3. Typed Clients

It provides the same capabilities as the named client but instead of identifying the client based on the key it actually intelligently resolve HttpClient in a specific HttpHandler class. Another advantage of this approach is you don't need to create HttpClient manually using IHttpClientFactory.CreateClient() method.

Let's say you decided to write one custom HttpHandler which would be the single source for every REST call, so your implementation would look something like below.

HttpCommands.cs:

public class HttpCommands
{
  private readonly HttpClient _httpClient;

  public HttpCommands(HttpClient httpClient)
  {
    _httpClient = httpClient;
  }
}
Enter fullscreen mode Exit fullscreen mode

Look at the constructor we are injecting HttpClient itself and not IHttpClientFactory, which means _factory.CreateClient() is not required anymore because .Net core is smart enough to resolve the dependency.

Startup.cs: To register typed HttpClient using generic version of AddHttpClient() extension method.

services.AddHttpClient<HttpCommands>();

//Or if you need custom configuration you can configure that too as we did for the named client

services.AddHttpClient<HttpCommands>(client =>
{
  client.BaseAddress = new Uri("https://apiA.com/");
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

  • What are the issues in traditional HttpClient usage?
  • What are the issues with the static or singleton HttpClient instance.
  • What are the different ways to use the IHttpClientFactory.

Happy Coding!

Top comments (0)