A few years back I wrote the post .NET HttpClient – the correct way to use it.
I wanted to extend this discussion to the other ways of using/instantiating your HttpClient.
We’ll look at this from the view of the way we’d usually configure things for ASP.NET but these are not limited to ASP.NET.
IHttpClientFactory
Instead of passing an HttpClient to a class (such as a controller) we might prefer to use the IHttpClientFactory. This allows us to inject the factory and create an instance of an HttpClient using the method CreateClient, for example in our Program.cs
builder.Services.AddHttpClient();
then in our code which uses the IHttpClientFactory
public class ExternalService(IHttpClientFactory httpClientFactory)
{
public Task LoadData()
{
var httpClient = httpClientFactory.CreateClient();
// use the httpClient as usual
}
}
This might not seem that much of an advancement from passing around HttpClient’s.
Where this is really useful is in allowing us to configure our HttpClient, such as base address, timeouts etc. In this situation we can use “named” clients. We’d assign a name to the client such as
builder.Services.AddHttpClient("external", client => {
client.BaseAddress = new Uri("https://some_url");
client.Timeout = TimeSpan.FromMinutes(3);
});
Now in usage we’d write the following
var httpClient = httpClientFactory.CreateClient("external");
We can now configured multiple clients with different names for use in different scenarios. With can also add policy and message handlers, for example
builder.Services.AddHttpClient("external", client => {
// configuration for the client
}).
AddHttpMessageHandler<SomeMessageHandler>()
.AddPolicyHandler(SomePolicy());
Strongly Typed Client
Typed or Strongly Typed clients is another way of using the HttpClient, weirdly this looks much more like our old way of passing HttpClient’s around.
We create a class specific to the HttpClient usage, and have an HttpClient parameter on the constructor, for example
public class ExternalHttpClient : IExternalHttpClient
{
private readonly HttpClient _httpClient;
public ExternalHttpClient(HttpClient httpClient)
{
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri("https://some_url");
_httpClient.Timeout = TimeSpan.FromMinutes(3);
}
public Task<SomeData> GetDataAsync()
{
// use _hhtpClient as usual
}
}
We’d now need to add the client to the dependency injection in Program.cs using
builde.Services.AddHttpClient<IExternalHttpClient, ExternalHttpClient>();
Conclusions
The first question might be, why use strongly typed HttpClient’s over IHttpClientFactory. The most obvious response is that it gives a clean design, i.e. we don’t use “magic strings” we know which client does what as it includes the methods to call the endpoints for the developer. Essentially it encapsulates our HttpClient usage for a specific endpoint. It also gives us a cleaner way of testing our code by allowing us to mock the interface only (not having to mock an IHttpClientFactory etc.).
However the IHttpClientFactory way of working gives us central place where we’d generally have all our clients declared and configured, named clients allow us to switch between clients easily using the name, it also gives great integrations for things like Polly.