Microsoft introduced the HttpClient in .Net Framework 4.5 and is the most popular way to consume a Web API in your .NET server-side code. But it has some serious issues like disposing the HttpClient object doesn’t close the socket immediately, too many instances affecting the performance and Singleton HttpClient or shared HttpClient instance not respecting the DNS Time to Live (TTL) settings. HttpClientFactory solves the all these problems. It is one of the newest feature of ASP.NET Core 2.1. It provides a central location for naming and configuring and consuming logical HttpClients in your application, and this post talks about 3 ways to use HTTPClientFactory in ASP.NET Core 2.1.
3 ways to use HTTPClientFactory in ASP.NET Core 2.1
There are 3 different ways to use it and we’ll see an example of each of them.
- Using HttpClientFactory Directly
- Named Clients
- Typed Clients
Using HttpClientFactory Directly
Irrespective of the above mention way you choose, you’ll always have to register the HttpClient in ConfigureServices
method of the Startup.cs
class. The following line of code registers HttpClient with no special configuration.
1
|
services.AddHttpClient();
|
You can use it in the following way in the API controller.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
ValuesController : Controller
{
private
readonly
IHttpClientFactory _httpClientFactory;
public
ValuesController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet]
public
async
Task<ActionResult> Get()
{
var
client = _httpClientFactory.CreateClient();
string
result =
await
client.GetStringAsync(
"/"
);
return
Ok(result);
}
}
|
Named Clients
The basic use of HTTPClientFactory in above example is ideal in a situation where you need to make a quick request from a single place in the code. When you need to make multiple requests from multiple places from your code, “Named Clients” will help you. With named clients, you can define the HTTP client with some pre-configured settings which will be applied when creating the HttpClient. Like,
1
2
3
4
5
6
7
|
services.AddHttpClient();
services.AddHttpClient(
"github"
, c =>
{
c.DefaultRequestHeaders.Add(
"Accept"
,
"application/vnd.github.v3+json"
);
c.DefaultRequestHeaders.Add(
"User-Agent"
,
"HttpClientFactory-Sample"
);
});
|
Here we call AddHttpClient
twice, once with the name ‘github’ and once without. The github client has some default configuration applied, namely the base address and two headers required to work with the GitHub API. The overload of AddHttpClient
method accepts two parameters, a name and an Action delegate taking a HttpClient which allows us to configure the HttpClient.
You can use named client in the following way in the API controller.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
class
ValuesController : Controller
{
private
readonly
IHttpClientFactory _httpClientFactory;
public
ValuesController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet]
public
async
Task<ActionResult> Get()
{
var
client = _httpClientFactory.CreateClient(
"github"
);
string
result =
await
client.GetStringAsync(
"/"
);
return
Ok(result);
}
}
|
Here, we are passing the registered name of the client in CreateClient()
method to create HttpClient. This is useful as the default configuration defined at the time of registration will be pre-applied when we ask for a named client.
Typed Client
Using Typed clients, you can define pre-configuration for your HttpClient inside a custom class. This custom class can be registered as Typed client, and later when needed, it can be injected via the calling class constructor. I prefer Typed Client for the following reasons,
- Flexible approach compare to named clients.
- You no longer have to deal with strings (like in named clients).
- You can encapsulate the HTTP calls and all logic dealing with that endpoint.
Let’s see an example. Below is a custom class defined for Github client.
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
GitHubClient
{
public
HttpClient Client {
get
;
private
set
; }
public
GitHubClient(HttpClient httpClient)
{
httpClient.DefaultRequestHeaders.Add(
"Accept"
,
"application/vnd.github.v3+json"
);
httpClient.DefaultRequestHeaders.Add(
"User-Agent"
,
"HttpClientFactory-Sample"
);
Client = httpClient;
}
}
|
You can register this as a typed client using the following line.
1
|
services.AddHttpClient<GitHubClient>();
|
And, use it in the following way in the API controller.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
class
ValuesController : Controller
{
private
readonly
GitHubClient _gitHubClient;;
public
ValuesController(GitHubClient gitHubClient)
{
_gitHubClient = gitHubClient;
}
[HttpGet]
public
async
Task<ActionResult> Get()
{
string
result =
await
_gitHubClient.client.GetStringAsync(
"/"
);
return
Ok(result);
}
}
|
This works great. There is another better way of making typed client work. Here, the HttpClient is exposed directly, but you can encapsulate the HttpClient entirely using the following way. First, define a contract for the GitHubClient
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public
interface
IGitHubClient
{
Task<
string
> GetData();
}
public
class
GitHubClient : IGitHubClient
{
private
readonly
HttpClient _client;
public
GitHubClient(HttpClient httpClient)
{
httpClient.DefaultRequestHeaders.Add(
"Accept"
,
"application/vnd.github.v3+json"
);
httpClient.DefaultRequestHeaders.Add(
"User-Agent"
,
"HttpClientFactory-Sample"
);
_client = httpClient;
}
public
async
Task<
string
> GetData()
{
return
await
_client.GetStringAsync(
"/"
);
}
}
|
Register this as a typed client using the following line.
1
|
services.AddHttpClient<IGitHubClient, GitHubClient>();
|
And, use it in the following way in the API controller.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
class
ValuesController : Controller
{
private
readonly
IGitHubClient _gitHubClient;;
public
ValuesController(IGitHubClient gitHubClient)
{
_gitHubClient = gitHubClient;
}
[HttpGet]
public
async
Task<ActionResult> Get()
{
string
result =
await
_gitHubClient.GetData();
return
Ok(result);
}
}
|
This approach also makes unit testing easy while testing HttpClients as you no longer have to mock them.
That’s it. You can read all my ASP.NET Core 2.1 post here.
Recommended Reading:
- HttpClientFactory in ASP.NET Core 2.1 (Part 1) : Steve Gordon
- ASP.NET Core 2.1-preview1: Introducing HTTPClient factory
Summary
To summarize, this post talks about all 3 possible ways to use HttpClientFactory with ASP.NET Core 2.1. I prefer to use TypedClient as it’s a lot more flexible, provides encapsulation for HttpClient and makes it easy to test the code. HttpClientFactory is great addition to ASP.NET Core 2.1 as it addresses the problems of HttpClient.
Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section.