HttpClientFactory has been around the .NET ecosystem for a few years now.
In this post we will look at 3 basic implementations of HttpClientFactory; basic, named, and typed.
All the code in this post is available in this GitHub repository.
First, let's learn about what HttpClient is, how HttpClientFactory fits into the picture and why we would want to use it.
Why can’t I just use HttpClient?
You can of course just use the HttpClient, but let's look a little more closely at what HttpClient is actually doing.
The following diagram from the Microsoft documentation shows the relationship between the client and the handlers:
The default handler, `HttpClientHandler` actually sends the request over the network and gets the response from the server. Custom message handlers can be inserted into the client pipeline if required.
You may be familiar with an implementation of HttpClient similar to the following in your web projects:
There is nothing inherently wrong with the above code when considered in isolation.
HttpClient implements IDisposable which sees many developers creating it within a `using` block. Then, once out of scope, it will be properly disposed of. However, in the blog “You’re using HttpClient wrong and it’s destabilizing your software” on “ASP.NET Monsters'' you can see that this is not the ideal way to proceed.
However, this comes with its own set of issues. For example, the client will keep connections open for the lifespan of the application, it won't respect the DNS TTL settings and it will never get DNS updates. So this isn't a perfect solution either.
What is the HttpClientFactory?
The primary and authoritative reference material is from the Microsoft Documentation.
This documentation describes HttpClientFactory as being "an opinionated factory for creating HttpClient instances to be used in your applications”.
HttpClientFactory middleware was created partly to try to address some of the issues raised above.
HttpClientFactory Key Features
So what are some of the key features? Again drawing on the reference material we can learn that:
- It provides a central place to name and configure our HttpClients.
- Delegates handlers in HttpClient and implements Polly-based middleware to take advantage of Polly’s policies for resiliency.
- HTTP clients are registered in a factory
- Can use a Polly handler that allows Polly policies to be used for better resiliency
- It manages the lifetime of HttpClientHandlers to avoid the aforementioned issues with trying to handle HttpClient lifetimes yourself
In this blog we'll be looking at several ways to implement the HttpClientFactory using the AssemblyAi API:
- Use `HttpClientFactory` directly
- Use named clients
- Use typed clients
Basic HttpClientFactory usage
A basic HttpClientFactory can be instanced via Dependency Injection.
First we will need to add the following code to the `Startup` class within the `ConfigureServices` method:
Then, in the class in which you wish to make a REST call, you can request the HttpClientFactory. We will show this in an API Controller:
The client factory will handle the disposal of the HttpClient created in the above code.
Named HttpClientFactory clients
The previous code enables you to define the HttpClient at time of use. However, client configuration, such as an API endpoint URL, can be defined in one place within the `Startup` class. The named client can then be requested via dependency injection making the code more reusable. This is great if you have many distinct uses of HttpClient or if you have many clients with different configurations.
The code below shows how we name and define an HttpClient within the `Startup` class.
The named HttpClient is consumed in a similar manner as the basic method, only this time we need to request the named instance from the factory. We also use the `SendAsync` API instead, so setting up the request is a little different.
Whenever we call `_clientFactory.CreateClient("AssemblyAIClient");` a new client is created with all the predefined configuration, so there is no need to define it within the method making the request.
Typed HttpClientFactory clients
Typed clients are very similar to named clients, but rather than using a string as a key, we instead take advantage of strong types. . This improves our code by avoiding the use of potentially brittle strings also means that intellisense and compiler support are available to help when creating clients.
A typed client is a great way to encapsulate all of the logic, therefore keeping the `Startup` class cleaner and easier to read and maintain.
To create a typed client, we will need to first create a class that is used to encapsulate our logic. We will call it `AssemblyAiService` in this instance.
In the above code, we have created a simple method that accepts `StringContent` and then posts that to the AssemblyAI endpoint. The method returns the JSON string response.
We will need to configure the client in the `Startup` class just as we have done in the previous examples.
As you can see, it is now much easier to read and understand what service has been added when using a typed HttpClient.
Consumption of the client in the controller is also nicely encapsulated, extracting a lot of the boilerplate code away into a service.
The above code is also much more testable as it follows the SOLID principles more closely.
I hope the code above has inspired you to try out HttpClientFactory in your next project.
There are a lot more benefits to HttpClientFactories such as Policy Management with policies from the Polly Register and combining third-party libraries into generated clients.
All of the above code can be found in this GitHub repository.
I love how clean my code is when I use Typed clients, I hope you do too!
If you have any questions or ideas, then I would love to hear from you!