下の図の構成で、ASP.NET MVC5 において Dependency Injection (DI) により IHttpClientFactory を取得し、それから HttpClient を生成して Web API からデータを取得し、そのデータをブラウザに表示する方法を書きます。

ASP.NET Core MVC の例は先の記事「ASP.NET と HttpClient (CORE)」に書きました。
ASP.NET Core の場合は、Visual Studio のテンプレートを使ってプロジェクトを作成すればデフォルトで DI 機能が実装されるので、Program.cs で AddHttpClient メソッドを使って IHttpClientFactory を IServiceCollection (DI コンテナ) に登録するだけで、Controller はコンストラクタの引数経由で IHttpClientFactory を取得できます。
一方、ターゲットフレームワークが .NET Framework 場合、Visual Studio のテンプレートで作成した MVC5 アプリには DI 機能は実装されません。なので、最初の課題はそれにどのように DI 機能を追加するかになります。
ちなみに DI を利用して IHttpClientFactory を取得するのは必須です。何故かと言うと、Microsoft のドキュメント「IHttpClientFactory の代替手段」に書いてありますように、以下のことを回避できるというメリットがあるからです。
-
HttpMessageHandler インスタンスをプールすることによるリソース枯渇の問題
-
一定の間隔で HttpMessageHandler インスタンスを循環させることによって発生する古くなった DNS の問題
ターゲットフレームワークが .NET Framework のアプリでも、バージョンが 4.6.2 以降であれば ASP.NET Core で DI に使われている Microsoft.Extensions.DependencyInjection 名前空間にあるクラス類を利用して DI 機能を実装できます。
.NET Framework 4.8 の MVC5 アプリに DI 機能を実装する詳しい方法は、先の記事「MVC5 での Dependency Injection」に書きました。この記事では、その記事のアプリの IServiceCollection (DI コンテナ) に IHttpClientFactory を登録し、それを Controller でどのように使って Web API に要求を出し、データを取得するかを書きます。
(1) Microsoft.Extensions.Http
NuGet パッケージ Microsoft.Extensions.Http をインストールします。これは AddHttpClient メソッドを使用して IHttpClientFactory を IServiceCollection(DI コンテナ)に登録できるようにするため必要です。

(2) AddHttpClient メソッドの追加
先の記事「MVC5 での Dependency Injection」に書いた Global.asax.cs のコード内の ConfigureServices メソッドに、以下のように AddHttpClient メソッドを追加します。この一行で Controller はコンストラクタの引数経由で IHttpClientFactory を受け取れるようになります。
// DI コンテナにサービスを登録するメソッド
private void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ScopedThing>();
services.AddTransient<HomeController>();
// IHttpClientFactory を DI して利用できるよう追加
services.AddHttpClient();
}
(3) Controller
Controller の例です。ブラウザが Hero アクションメソッドを呼び出すと、DI 機能により IHttpClientFactory が Controller のコンストラクタの引数経由で渡されます。
IHttpClientFactory から生成した HttpClient を使って Web API に要求を出して JSON 形式のデータを取得し、それを .NET の List<Hero> 型のオブジェクトにデシリアライズして View に渡しています。
using Mvc5DependencyInjection.Models;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Text.Json;
namespace Mvc5DependencyInjection.Controllers
{
public class HomeController : Controller
{
private readonly ScopedThing _scopedThing;
// IHttpClientFactory を DI して利用できるよう追加
private readonly IHttpClientFactory _clientFactory;
// コンストラクタの引数経由 IHttpClientFactory が DI される
public HomeController(ScopedThing scopedThing,
IHttpClientFactory clientFactory)
{
this._scopedThing = scopedThing;
this._clientFactory = clientFactory;
}
// ・・・中略・・・
public async Task<ActionResult> Hero()
{
// IHttpClientFactory から HttpClient を取得
HttpClient client = _clientFactory.CreateClient();
var url = "Web API の url";
var request = new HttpRequestMessage(HttpMethod.Get, url);
HttpResponseMessage response = await client.SendAsync(request);
List<Hero> list = null;
if (response.IsSuccessStatusCode)
{
using (Stream responseStream =
await response.Content.ReadAsStreamAsync())
{
list = await JsonSerializer.
DeserializeAsync<List<Hero>>(responseStream);
}
}
return View(list);
}
}
}
(4) Model
namespace Mvc5DependencyInjection.Models
{
public class Hero
{
public int id { get; set; }
public string name { get; set; }
}
}
(5) View
@model IEnumerable<Mvc5DependencyInjection.Models.Hero>
@{
ViewBag.Title = "Hero";
}
<h2>Hero</h2>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.id)
</th>
<th>
@Html.DisplayNameFor(model => model.name)
</th>
</tr>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.id)
</td>
<td>
@Html.DisplayFor(modelItem => item.name)
</td>
</tr>
}
</table>
(6) 実行結果
上のコードを実行するとブラウザには以下のように表示されます。
