ASP.NET Core 2.1 中的 HttpClientFactory (Part 2) 定義命名化和類型化的客戶端

原文:https://www.stevejgordon.co.uk/httpclientfactory-named-typed-clients-aspnetcore  
發表於:2018年1月git

      上一篇文章《HttpClientFactory簡介》我解釋了建立該功能的緣由。咱們知道了它能夠解決的問題,而後例舉了一個很是基本的示例展現瞭如何在WebAPI應用程序中使用它。在這篇文章中,我想深刻探討另外兩種可使用它的方法:命名化客戶端(named clients)和類型化客戶端(typed clients)。github

命名化客戶端(Name Clients)

      在第一篇文章中,我演示瞭如何使用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");
});

      這很是好,但咱們能夠更進一步,來看看如何使用自定義的類型化客戶端。測試

類型化客戶端(Typed Clients)

      類型化客戶端容許咱們定義一個經過構造函數注入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(Encapsulating the HttpClient)

      最後一個例子是咱們想要徹底封裝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)的另外一種模式。

相關文章
相關標籤/搜索