ASP.NET Core 2.1-preview1 で登場した HttPClientFactory について、今更ですが整理しました。
Build 2018 で HTTPClient factory 周りの話は...フレームワークが全体的に RC1 になって Go Live になったくらいでだった(ですよね?)のでこのタイミングで基本的な内容だけは書いておこうと。
Overview
- HttpClientFactory について
- 準備:Nuget パッケージ
- 実装
1. 単に HttpClient の Factory として使う
2. Named Clients
3. Typed Clients
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」と入力して、現時点ではプレリリース状態なので、プレリリースを含める
にチェックを入れると表示されます。インストールしましょう。
ちなみに、この Nuget パッケージの依存関係は、.NET Standard, Version=v2.0なので、現時点(2018-05)で最新の VS2017 にしておけば、別途SDK入れたりすることなく利用できますが、そこらへんも激動の時期なので、ご注意ください。
余談ですが、上の画像で表示されているパッケージの Polly についても、使うのがデファクトスタンダードになると思うので(もうなってるか)、気がむ向いたら書いておきたいなーと思ってます。
実装
ASP.NET Core の Web アプリ(MVCのやつ)のプロジェクトを作って使いながら動作確認をします(作成方法は公式ドキュメントにて)。
プロジェクトのターゲットフレームワークは、.NET Core 2.0 で進めています。
HttpClientFactory を使う際の基本的な流れは以下です。
- 設定(DI Container に登録とか)
- 使う場所で呼び出す
では、冒頭で述べた以下の実装パターンを試していきまっす♪
- 単に HttpClient の Factory として使う
- Named Clients
- 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
をたたいてみるサンプルです。
今回は、デフォルトで生成される HomeController
の About
メソッドを読んで動作確認をする流れでいきます。コードは以下の通りです。
コードでいじったところは以下くらいです。
- (10行目)
IHttpClientFactory
の変数を定義 - (12 - 15行目)恒例のコンストラクターで
IHttpClientFactory
のインスタンスを取得 - (24 - 25行目)
HttpClient
を Factory 経由で作って使う
今回のコードでは23行目の About
で HttpClient
使っているので、デバッグ実行した際は、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.」 って文がなんか気になったけど...まいっか。
他に試したいことがいくつかありますが今日はこの辺で。