原文:https://www.stevejgordon.co.uk/httpclientfactory-named-typed-clients-aspnetcore
發表於:2018年1月git
上一篇文章《HttpClientFactory簡介》我解釋了建立該功能的緣由。咱們知道了它能夠解決的問題,而後例舉了一個很是基本的示例展現瞭如何在WebAPI應用程序中使用它。在這篇文章中,我想深刻探討另外兩種可使用它的方法:命名化客戶端(named clients)和類型化客戶端(typed clients)。github
在第一篇文章中,我演示瞭如何使用HttpClientFactory來獲取基本的HttpClient實例。當您只須要從單一位置發出快速請求時,這很好。但一般,您可能但願從代碼中的多個位置向同一服務發出多個請求。
經過命名化客戶端的概念,HttpClientFactory使這一點變得更容易。使用命名化客戶端,您能夠建立一個註冊,其中包含在建立HttpClient時的一些特定配置。您能夠註冊多個命名化客戶端,每一個客戶端均可以預先配置不一樣的設置。
爲了讓這個概念更具體一些,讓咱們看一個例子。在個人Startup.ConfigureServices方法中,使用AddHttpClient的不一樣重載方法,該方法接受兩個附加參數。把一個名稱和一個Action委託「告訴」HttpClient。ConfigureServices代碼:json
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("GitHubClient", client => { client.BaseAddress = new Uri("https://api.github.com/"); client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting"); }); services.AddMvc(); }
第一個字符串參數是用於此客戶端註冊的名稱。Action <HttpClient>委託容許咱們在爲咱們構造HttpClient時配置它們。這很是方便,由於咱們能夠預先定義一個基地址和一些已知的請求頭。當咱們請求命名化客戶端時,會爲咱們建立一個新客戶端,而且每次都會應用此配置。
使用的時候,CreateClient根據名稱來請求一個客戶端:api
[Route("api/[controller]")] public class ValuesController : Controller { private readonly IHttpClientFactory _httpClientFactory; public ValuesController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } [HttpGet] public async Task<ActionResult> Get() { var client = _httpClientFactory.CreateClient("GitHubClient"); var result = await client.GetStringAsync("/"); return Ok(result); } }
在這個例子中,咱們建立的HttpClient實例已經有基本地址集(base address set),因此咱們的GetStringAsync方法傳入對應的URI便可。app
這種命名化的方式使咱們可以控制應用於HttpClient的配置。我不是「魔力字符串」的忠實粉絲,因此若是我使用命名客戶端,我可能會有一個靜態類,其中包含客戶端名稱的字符串常量。像這樣:async
public static class NamedHttpClients { public const string GitHubClient = "GitHubClient"; }
註冊(或請求)客戶端時,咱們可使用靜態類值,而不是「魔力字符串」:函數
services.AddHttpClient(NamedHttpClients.GitHubClient, client => { client.BaseAddress = new Uri("https://api.github.com/"); client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting"); });
這很是好,但咱們能夠更進一步,來看看如何使用自定義的類型化客戶端。測試
類型化客戶端容許咱們定義一個經過構造函數注入HttpClient的自定義類。這樣咱們可使用IHttpClientBuilder的擴展方法連接DI系統,或者使用泛型AddHttpClient方法來接收自定義類型。一旦咱們有了自定義類,咱們就能夠直接公開HttpClient,也能夠將Http calls封裝在特定方法中,從而更好地定義外部服務的使用。這種方法也意味着咱們再也不須要「魔術字符串」,而且看起來更加合理。
讓咱們看一個自定義類型化客戶端的基本例子:ui
public class MyGitHubClient { public MyGitHubClient(HttpClient client) { Client = client; } public HttpClient Client { get; } }
這個類須要在構造函數中接受做爲參數的HttpClient。如今,咱們已經爲HttpClient的實例設置了一個公共屬性。
而後,咱們須要在ConfigureServices中註冊:spa
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient<MyGitHubClient>(client => { client.BaseAddress = new Uri("https://api.github.com/"); client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting"); }); services.AddMvc(); }
咱們將MyGitHubClient做爲泛型參數傳遞給AddHttpClient。它在DI系統中被註冊爲transient scope。因爲咱們的自定義類接受HttpClient,所以相關聯的「工廠」會建立一個適當配置的HttpClient實例,並注入它。如今能夠更新控制器以接受咱們的類型化客戶端而不是IHttpClientFactory:
[Route("api/[controller]")] public class ValuesController : Controller { private readonly MyGitHubClient _gitHubClient; public ValuesController(MyGitHubClient gitGitHubClient) { _gitHubClient = gitGitHubClient; } [HttpGet] public async Task<ActionResult> Get() { var result = await _gitHubClient.Client.GetStringAsync("/"); return Ok(result); } }
因爲咱們自定義的類型化客戶端經過屬性公開了HttpClient,所以咱們能夠直接使用它進行HTTP調用。
最後一個例子是咱們想要徹底封裝HttpClient的狀況。當咱們想要定義處理對端點的特定調用的方法時,最有可能使用此方法。此時,咱們還能夠在每一個方法中封裝響應和反序列化的驗證,以便在單一位置處理它。
public interface IMyGitHubClient { Task<int> GetRootDataLength(); } public class MyGitHubClient : IMyGitHubClient { private readonly HttpClient _client; public MyGitHubClient(HttpClient client) { _client = client; } public async Task<int> GetRootDataLength() { var data = await _client.GetStringAsync("/"); return data.Length; } }
這種狀況下,咱們經過private readonly字段存儲了在構造中注入的HttpClient。與直接經過此類(class)得到HttpClient不一樣,咱們提供了一個GetRootDataLength方法來執行Http調用並返回請求長度。一個簡單的例子,但你應該已經明白了!
咱們如今能夠更新控制器以接受和使用接口,以下所示:
[Route("api/[controller]")] public class ValuesController : Controller { private readonly IMyGitHubClient _gitHubClient; public ValuesController(IMyGitHubClient gitHubClient) { _gitHubClient = gitHubClient; } [HttpGet] public async Task<ActionResult> Get() { var result = await _gitHubClient.GetRootDataLength(); return Ok(result); } }
咱們如今能夠調用接口上定義的GetRootDataLength方法,而無需直接與HttpClient交互。這對測試很是有用,如今能夠在咱們想要測試這個控制器時輕鬆模擬IMyGitHubClient。過去測試HttpClient有點痛苦,按照我一般習慣的方式會有更多代碼。
在DI容器中註冊,ConfigureServices變爲:
services.AddHttpClient<IMyGitHubClient, MyGitHubClient>(client => { client.BaseAddress = new Uri("https://api.github.com/"); client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); client.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactoryTesting"); });
AddHttpClient有一個接受兩個泛型參數的簽名,對應DI中的簽名。
在這篇文章中,咱們探討了HttpClientFactory一些更高級的方法,它容許咱們使用特定的命名配置建立不一樣的HttpClient實例。而後咱們討論了使用類型化客戶端,經過擴展實現了咱們本身的類,它接受HttpClient實例。咱們能夠直接公開HttpClient,也能夠將調用封裝到此類中來訪問遠程端點。 下一篇文章,咱們將討論使用DelegatingHandlers來實現「傳出請求中間件」( outgoing request middleware)的另外一種模式。