ASP.NET CoreでのIHttpClientFactoryを利用したHTTPリクエスト実装
IHttpClientFactoryはサードパーティライブラリ(例えばRefit)と組み合わせて使用できます。Refitは.NET用のRESTライブラリであり、REST APIをライブインターフェースに変換します。AddRefitClientを呼び出すことで、HttpClientを使用して外部HTTP呼び出しを行うインターフェースの動的実装を生成します。
外部APIを表現するカスタムインターフェースの定義
public interface IGitHubApiClient
{
[Get("/repositories/dotnet/aspnet-core-docs/branches")]
Task<IEnumerable<RepositoryBranch>> GetDocumentationBranchesAsync();
}
AddRefitClientを呼び出して動的実装を生成し、ConfigureHttpClientでHttpClientを構成
builder.Services.AddRefitClient<IGitHubApiClient>()
.ConfigureHttpClient(httpClient =>
{
httpClient.BaseAddress = new Uri("https://api.github.com/");
// Microsoft.Net.Http.Headersを使用
// GitHub APIは2つのヘッダーを必要とします
httpClient.DefaultRequestHeaders.Add(
HeaderNames.Accept, "application/vnd.github.v3+json");
httpClient.DefaultRequestHeaders.Add(
HeaderNames.UserAgent, "DotNetApiClientSample");
});
DIを介してIGitHubApiClientの動的実装にアクセス
public class GitHubDataModel : PageModel
{
private readonly IGitHubApiClient _apiClient;
public GitHubDataModel(IGitHubApiClient apiClient) =>
_apiClient = apiClient;
public IEnumerable<RepositoryBranch>? Branches { get; set; }
public async Task OnGet()
{
try
{
Branches = await _apiClient.GetDocumentationBranchesAsync();
}
catch (ApiException)
{
// エラーハンドリング
}
}
}
実装例:ユーザーAPIクライアント
パッケージ参照:
<PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
注意:GetやPostなどの関数はTask非同期形式である必要があります。
using Refit;
namespace ApiIntegration.Client
{
public interface IUserService
{
[Get("/api/users/{username}")]
Task<string> GetUserNameAsync(string username);
[Post("/api/users")]
Task<UserInfo> CreateUserAsync(UserInfo userData);
}
}
サービスの登録と構成
builder.Services.AddRefitClient<IUserService>().ConfigureHttpClient(httpClient =>
{
httpClient.BaseAddress = new Uri("http://localhost:5255/api");
httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, "ApiClient");
});
コントローラーでの使用例
using ApiIntegration.Client;
using ApiIntegration.Models;
using Microsoft.AspNetCore.Mvc;
namespace ApiIntegration.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
[HttpGet("{name}")]
public async Task<IActionResult> GetUser(string name)
{
string userName = await _userService.GetUserNameAsync(name);
return Ok(userName);
}
[HttpPost]
public async Task<IActionResult> CreateUserInfo(UserInfo userInfo)
{
UserInfo createdUser = await _userService.CreateUserAsync(userInfo);
return Ok(createdUser);
}
}
}
拡張メソッドによるカスタム設定
public static class RefitClientExtensions
{
public static IServiceCollection AddCustomRefitClient<T>(this IServiceCollection services, Uri serviceUri) where T : class
{
services.AddRefitClient<T>().ConfigureHttpClient(httpClient =>
{
httpClient.BaseAddress = serviceUri;
});
return services;
}
public static IServiceCollection AddCustomRefitClient<T>(this IServiceCollection services, Action<HttpClient> configurationAction) where T : class
{
services.AddRefitClient<T>().ConfigureHttpClient(configurationAction);
return services;
}
}
拡張メソッドの使用例
// URIを指定して登録
builder.Services.AddCustomRefitClient<IUserService>(
new Uri("http://localhost:5257/api"));
// 構成アクションを指定して登録
builder.Services.AddCustomRefitClient<IUserService>(httpClient =>
{
httpClient.BaseAddress = new Uri("http://localhost:5257/api");
httpClient.Timeout = TimeSpan.FromSeconds(30);
});
Refit.HttpClientFactoryの核心機能とベストプラクティス
主要機能
- 宣言型APIインターフェース:インターフェースメソッド([Get]、[Post]など)を使用してHTTPリクエストを定義し、自動で型付きクライアントコードを生成します。手動のシリアライズ/デシリアライズ操作を削減します。
- HttpClient設定の集中化:ConfigureHttpClient()を使用してBaseAddress、タイムアウトなどのパラメータを一元管理し、分散されたHttpClientインスタンスの管理を回避します。
- ライフサイクル管理の最適化:IHttpClientFactoryの接続プールと自動リトライメカニズムを統合し、高負荷環境でのパフォーマンスと安定性を向上させます。
- ミドルウェア拡張サポート:AddHttpMessageHandler()を使用してカスタムDelegatingHandler(認証、ログ、リトライ戦略など)を注入できます。
基本的な使用手順(.NET 6+を例に)
1. NuGetパッケージのインストール
bash
dotnet add package Refit
dotnet add package Refit.HttpClientFactory
2. APIインターフェースの定義
public interface IUserApiService
{
[Get("/users/{userId}")]
Task<UserProfile> GetUserProfileAsync(int userId);
[Post("/users")]
Task<UserProfile> CreateUserAsync(CreateUserRequest request);
}
3. サービス登録と構成
builder.Services
.AddRefitClient<IUserApiService>()
.ConfigureHttpClient(client => {
client.BaseAddress = new Uri("https://api.example.com");
client.Timeout = TimeSpan.FromSeconds(30);
})
.AddHttpMessageHandler<AuthenticationHandler>(); // 認証ミドルウェアの追加
4. カスタムミドルウェアの例(トークン注入)
public class AuthenticationHandler : DelegatingHandler
{
private readonly string _apiKey;
public AuthenticationHandler(string apiKey)
{
_apiKey = apiKey;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _apiKey);
return await base.SendAsync(request, cancellationToken);
}
}
ベストプラクティス
- 名前付きクライアントの使用:名前付きクライアントを使用して異なるAPIクライアントを区別し、細かな設定を実現します。
- エラーハンドリング:Pollyと組み合わせてサーキットブレーカーやリトライ戦略を実装します。
- パフォーマンス最適化:HttpClientインスタンスの頻繁な作成を避け、ファクトリ管理の再利用メカニズムを活用します。
- 設定の分離:APIエンドポイントや認証情報などの設定を構成ファイルや環境変数から読み込み、コードから分離します。
利点まとめ
- 型安全性:インターフェースを使用してAPIを定義することで、コンパイル時のエラー検出が可能になります。
- 保守性の向上:API呼び出しロジックをインターフェースにカプセル化し、コードの可読性と保守性を高めます。
- 接続管理の最適化:HttpClientFactoryがHttpClientインスタンスを効果的に管理し、リソース漏洩や接続問題を防ぎます。
- テスト容易性:インターフェースベースの設計により、モックやスタブを使用した単体テストが容易になります。
Refit.HttpClientFactoryを活用することで、REST APIとの連携フローを大幅に簡素化しながら、コードの保守性と拡張性を維持することができます。