BEACHSIDE BLOG

MicrosoftとかC#を好むレンジャーの個人的メモ

HttpClientFactory の使い方(ASP.NET Core 2.1-preview1 ~)

ASP.NET Core 2.1-preview1 で登場した HttPClientFactory について、今更ですが整理しました。

Build 2018 で HTTPClient factory 周りの話は...フレームワークが全体的に RC1 になって Go Live になったくらいでだった(ですよね?)のでこのタイミングで基本的な内容だけは書いておこうと。

Overview

HttpClientFactory について

ASP.NET Core 2.1-preview1 あたりから登場し、HttpClient のネーミングや構成をいい感じで管理できたり、HttpClient のライフサイクル(というか HttpClientMessageHandlers のライフサイクル)を管理するためのものですが、詳しくは、こちらの公式ブログの冒頭で書かれていることになります。

使い方としては、主に3つの使い方って感じでしょうか。

  • 単に HttpClient の Factory として使う
  • Named Clients
  • Typed Clients

それぞれについて簡単にみていきます。どう使うかは用途次第でしょうけど、過去のあれやこれやの問題を多少なりとも解決している以上、現時点で利用は必須かなーと思っています♪

準備:Nuget パッケージ

HttpClientFactory を利用するために、Nuget で Microsoft.Extensions.Http をインストールしましょう。

Visual Studio の上部のメニュー ツール > Nuget パッケージマネージャー > ソリューションの Nuget パッケージの管理 を開きます。
検索に「Microsoft.Extensions.Http」と入力して、現時点ではプレリリース状態なので、プレリリースを含める にチェックを入れると表示されます。インストールしましょう。

f:id:beachside:20180515110400p:plain

ちなみに、この Nuget パッケージの依存関係は、.NET Standard, Version=v2.0なので、現時点(2018-05)で最新の VS2017 にしておけば、別途SDK入れたりすることなく利用できますが、そこらへんも激動の時期なので、ご注意ください。

余談ですが、上の画像で表示されているパッケージの Polly についても、使うのがデファクトスタンダードになると思うので(もうなってるか)、気がむ向いたら書いておきたいなーと思ってます。


実装

ASP.NET Core の Web アプリ(MVCのやつ)のプロジェクトを作って使いながら動作確認をします(作成方法は公式ドキュメントにて)。
プロジェクトのターゲットフレームワークは、.NET Core 2.0 で進めています。

HttpClientFactory を使う際の基本的な流れは以下です。

  • 設定(DI Container に登録とか)
  • 使う場所で呼び出す

では、冒頭で述べた以下の実装パターンを試していきまっす♪

  1. 単に HttpClient の Factory として使う
  2. Named Clients
  3. Typed Clients


1. 単に HttpClient の Factory として使う

単に New してインスタンス化するのを DI してあげるだけです。これだけでも新たなライフサイクルの恩恵を得れるのでよいですが...まぁ現実的には、用途に合わせて後述の Named や Typed の使い方をしたいところですね。

IHttpClientFactory をDIし、そこから HttpClientインスタンスを取得します。

設定(DI Container に登録とか)

Startup.cs の ConfigureServices メソッドで、AddHttpClient 拡張メソッドを追加するだけです。

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient();
           
    services.AddMvc();
}

AddHttpClient メソッドでは、IHttpClientFactoryインスタンスを Singleton で設定とかを見ての通り色々やってますがここでは気にしなくて大丈夫です。

使う場所で呼び出す

適当にJSON を返してくれるサイト「JSON Test by jsontest」を使って、URL:http://echo.jsontest.com/id/1/value/one をたたいてみるサンプルです。

今回は、デフォルトで生成される HomeControllerAbout メソッドを読んで動作確認をする流れでいきます。コードは以下の通りです。

コードでいじったところは以下くらいです。

  • (10行目) IHttpClientFactory の変数を定義
  • (12 - 15行目)恒例のコンストラクターIHttpClientFactoryインスタンスを取得
  • (24 - 25行目)HttpClient を Factory 経由で作って使う

今回のコードでは23行目の AboutHttpClient 使っているので、デバッグ実行した際は、http://localhost:<ポート番号>/Home/About で正常に動くことを確認できます。
特になんの変哲もない利用方法です。

また、コンストラクターで取得しなくても、以下のコードのように16行目の引数に FromServices アトリビュートを付けてあげればメソッドからDIして呼び出すことももちろん可能です。


2. Named Clients

DI の構成時に、例えばBaseAddressとかRequestHeaderとか必要に応じて設定をし、それ自体に名前をつけ、利用する際に名前で呼び出すという使い方です。

IHttpClientFactory をDIし、そこから名前を指定して(色々設定済みの) HttpClientインスタンスを取得します。

設定(DI Container に登録とか)

先ほど同様、Startup.cs の ConfigureServices メソッドで、AddHttpClient 拡張メソッドを追加するだけですが、名前とかをを設定します。

今回の例では、21行目から28行目までで、BaseAddress と(なんとなく無意味に)Headerに関する情報を設定してます。
名前は、「JsonTestWeb」と名付けています。

使う場所で呼び出す

HttpClientFactory を DI でインスタンスを取得して、HttpClientFactory から名前付きで呼び出します。

HttpClientFactory の DI については前述してますので説明を省略しますが、HttpClient 自体の呼び方は、26行目のように CreateClient メソッドの引数に名前(JsonTestWeb)を入れるだけです。 BaseAddressは既にDIした際にセットされているので、それ以外を気にしてGetなりPostなりすればよい感じです。


3. TypedClients

型、つまりはクラスやインターフェースに対して DI をするってやつです(説明雑で微妙...)。

ここでは、先ほどまでやってた IHttpClientFactory ではなく HttpClient を直接 DI します。

HttpClient を DI するための型を作成

サンプルとして JsonTestWebService クラスを用意します。
(クラス名は、「Service」ってよりは「Client」って感じの方が責務として構成した方が良い設計になりそうな臭いがしそうですが、それはさおき。)

このクラスの中で HttpClient の変数があり、設定はこのクラス内で設定します。先ほどからちょっと違うのは、色気を出して ILogger を DI しだしました。

27行目の GetResultAsync メソッドでは、ここでも色気をだして引数で値を得てる以外は、基本的に先ほどの処理と同じようなことをしてます。

設定(DI Container に登録とか)

Startup.cs の ConfigureServices メソッドで、AddHttpClient 拡張メソッドにはこんな感じで。見る必要があるのは19行目だけです。

単純に HttpClient がインジェクトされるだけの設定には、AddHttpClient 拡張メソッドの Generics に登録です。

使う場所で呼び出す

よくある普通のDI方法で、先ほど作成した JsonTestWebService を呼び出すだけです。

11行目のコンストラクターインスタンスを呼び出しています。

21行目からのメソッドで先ほどの JsonTestWebService クラスの GetResultAsync メソッドを呼び出しているだけです。

余談ですが、http://echo.jsontest.com/APIは、日本語をなげると URL エンコードされた状態で帰ってくるので、WebUtility.UrlDecode とか使ってUrlDecodeしてあげる必要がありました♪

おわりに

今回は Polly に触れませんでしたがまたそのうち....。

現時点で個人的には Typed Client での利用が多い(むしろほぼこれ)ですが、 参考にした公式のこちらUsing Typed Clients で、 「Not everyone will want or be able to move configuration like this to the constructor of their Typed Client. It's shown here as an example of something that could be done, rather than a hard recommendation.」 って文がなんか気になったけど...まいっか。

他に試したいことがいくつかありますが今日はこの辺で。