3 ways to use HTTPClientFactory in ASP.NET Core 2.1

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();
         client.BaseAddress = new Uri( "http://api.github.com" );
         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.BaseAddress = new Uri( "https://api.github.com/" );
     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.BaseAddress = new Uri( "https://api.github.com/" );
         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.BaseAddress = new Uri( "https://api.github.com/" );
         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:

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.

PS: If you found this content valuable and want to return the favour, then Buy Me A Coffee
From: http://www.talkingdotnet.com/3-ways-to-use-httpclientfactory-in-asp-net-core-2-1/?utm_source=csharpdigest&utm_medium=email&utm_campaign=featured
相关文章
相关标签/搜索