The correct gesture for using HttpClient in NET Core

  • 2021-10-27 07:03:34
  • OfStack

Preface

To make it easier to invoke HTTP requests on the server side, Microsoft introduced HttpClient at. NET Framework 4. x. However, HttpClient has many serious problems, which have been criticized for a long time. For example, this article t. cn/Evzy80y of InfoQ spits out the problems that HttpClient cannot close the connection immediately and consumes serious performance.

I don't need to say more about the importance of Http protocol. Compared with URLConnection of traditional JDK, HttpClient increases ease of use and flexibility. It not only makes it easier for clients to send Http requests, but also facilitates developers to test interfaces (based on Http protocol), which improves development efficiency and code robustness. Therefore, mastering HttpClient is a very important compulsory content. After mastering HttpClient, I believe that the understanding of Http protocol will be deeper.

HttpClientFactory, introduced in NET Core 2.1, solves all the pain points of HttpClient. With HttpClientFactory, we don't need to care how to create HttpClient and how to release it. It allows you to create business-specific HttpClient, and can be friendly with the DI container in combination, more flexible.

Here are three new correct ways to use HttpClient, taking ASP. NET Core as an example.

1. Direct use mode

Either way, the service must be registered first. For ASP. NET Core applications, the ConfigureServices of the Startup. cs file is added if the code:


services.AddHttpClient();

IHttpClientFactory is then obtained by constructing an injection trial in Controller, and then an HttpClient object is created from it. Sample code:


public class ValuesController : BaseController
{
 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);
 }
}

This usage is suitable for one-time HTTP request call, but the disadvantage is that if you want to request the interface of github many times, you have to write a lot of repeated code to configure HttpClient.

2. How to use naming

The naming method can solve the problem of the first method. For HTTP calls of specific domain names, you can only configure them once, and then get the same Client by name in multiple places for use.

First, add the HttpClient service with a specific name where the Startup. cs registered the service, and add the required configuration, as shown in the following example:


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");
});

Note that AddHttpClient has been added twice, once unnamed and once named. It is used in Controller as follows:


public class ValuesController : BaseController
{
 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);
 }
}

However, if it is used frequently, there is still one trouble in this way of obtaining Client. The third method is introduced below, which can be used conveniently for 1 point, but the configuration is more troublesome.

3. Typed usage

Typed usage allows you to pre-place the configuration into a custom HttpClient, and then get an HttpClient instance directly through dependency injection wherever needed.

Let's look at specific examples. First, customize an HttClient class. For example, the class accessing the github interface can be defined as follows:


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;
 }

 public async Task<string> GetData()
 {
  return await _client.GetStringAsync("/");
 }
}

GetData here is a common method for additional extensions. You need to register the service for this class before using it:


services.AddHttpClient<GitHubClient>();

Then use it where you need it (such as Controller):


public class ValuesController : BaseController
{
 private readonly GitHubClient _gitHubClient;;

 public ValuesController(GitHubClient gitHubClient)
 {
  _gitHubClient = gitHubClient;
 }

 [HttpGet]
 public async Task<ActionResult> Get()
 {
  string result = await _gitHubClient.GetData();
  return Ok(result);
 }
}

Compared with the naming method, although it is troublesome to configure, it has two advantages: 1. Instead of obtaining Client through name string, it directly obtains specific Client through dependency injection; 2 is a common method that can be extended in a custom HttpClient class.

Summarize


Related articles: