1. 程式人生 > >.NET Core 中正確使用 HttpClient 的姿勢

.NET Core 中正確使用 HttpClient 的姿勢

為了更方便在服務端呼叫 HTTP 請求,微軟在 .NET Framework 4.x 的時候引入了 HttpClient。但 HttpClient 有很多嚴重問題,一直飽受詬病,比如 InfoQ 的這篇文章 t.cn/Evzy80y,吐槽了 HttpClient 不能立即關閉連線、效能消耗嚴重等的問題。

.NET Core 2.1 開始引入的 HttpClientFactory 解決了 HttpClient 的所有痛點。有了 HttpClientFactory,我們不需要關心如何建立 HttpClient,又如何釋放它。通過它可以建立具有特定業務的 HttpClient,而且可以很友好的和 DI 容器結合使用,更為靈活。下面以 ASP.NET Core 為例介紹 HttpClient 新的三種正確使用方式。

一、直接使用方式

不管是哪種方式,都要先註冊服務,對於 ASP.NET Core 應用則是在 Startup.cs 檔案的 ConfigureServices 新增如果程式碼:

services.AddHttpClient();

然後在 Controller 中通過構造注入的試獲得 IHttpClientFactory,然後通過它來建立 HttpClient 物件。示例程式碼:

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

這種使用方式適合一次性的 HTTP 請求呼叫,弊端是如果多次都要請求 github 的介面,那就得寫很多重複程式碼配置 HttpClient。

二、命名式使用方式

命名式的使用方式可以解決第一種方式的問題,對於特定域名的 HTTP 呼叫,可以只做一次配置,然後在多個地方通過名稱拿到相同的 Client 來使用。

首先在 Startup.cs 註冊服務的地方新增帶有特定名稱的 HttpClient 服務,並新增需要的配置,如下示例:

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

注意這裡添加了兩次 AddHttpClient,一次是沒有命名的,一次是有命名的。在 Controller 中的使用方式是:

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

但如果使用很頻繁,這種獲取 Client 的方式還是有一點麻煩。下面介紹第三種方式,可以在使用的時候方便一點,但配置要麻煩些。

三、型別化使用方式

型別化的使用方式可以預先把配置放到自定義的 HttpClient 中,然後在需要的地方都過依賴注入直接拿到 HttpClient 例項。

我們來看具體的例子,首先自定義一個 HttClient 類,比如訪問 github 介面的類可以這樣定義:

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 就是額外擴充套件的共用方法。使用前需要為這個類註冊服務:

services.AddHttpClient<GitHubClient>();

然後在需要的地方(比如 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);    }}

型別化的方式和命名式的方式相比,雖然在配置的時候會麻煩一點,但它有兩個好處:一是不用通過名稱字串來獲得 Client,而是直接通過依賴注入獲得具體的 Client;二是可以在自定義的 HttpClient 類中擴充套件共用的方法。