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

。NET Core 2。1 開始引入的 HttpClientFactory 解決了 HttpClient 的所有痛點。有了 HttpClientFactory,我們不需要關心如何建立 HttpClient,又如何釋放它。透過它可以建立具有特定業務的 HttpClient,而且可以很友好的和 DI 容器結合使用,更為靈活。下面以

http://

ASP。NET

Core 為例介紹 HttpClient 新的三種正確使用方式。

一、直接使用方式

不管是哪種方式,都要先註冊服務,對於

http://

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

然後在需要的地方(比如 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 類中擴充套件共用的方法。