業務アプリで外部サービスと連携する場合、 REST API × JSON が今の標準です。 C#では HttpClient を素で使う方法と、 インターフェイス駆動で書ける Refit がよく使われます。
この記事でわかること
・HttpClientの正しい使い方(DI・タイムアウト・JSON)
・GET / POST / PUT / DELETE の基本
・Refitによるインターフェイス駆動のAPIクライアント
・エラー処理・リトライ・タイムアウト設計
・業務アプリ向けのAPIクライアント設計パターン
・HttpClientの正しい使い方(DI・タイムアウト・JSON)
・GET / POST / PUT / DELETE の基本
・Refitによるインターフェイス駆動のAPIクライアント
・エラー処理・リトライ・タイムアウト設計
・業務アプリ向けのAPIクライアント設計パターン
1. HttpClient と Refit の位置づけ
| 項目 | HttpClient | Refit |
|---|---|---|
| 特徴 | 標準・低レベル・柔軟 | インターフェイス駆動・宣言的 |
| コード量 | 多い | 少ない |
| 制御性 | 高い(何でもできる) | 高いが抽象化される |
| 向き | 細かい制御・特殊ケース | 典型的なREST APIクライアント |
まずはHttpClientを理解 → その上でRefitを使う という順番が一番しっくりきます。
2. HttpClientの正しい使い方(DI前提)
HttpClientは「毎回 new して Dispose」するとポート枯渇・パフォーマンス劣化の原因になります。
HttpClientFactory(DI)で管理するのが現代の標準です。
■ 2-1. DI登録(.NET 6 以降)
builder.Services.AddHttpClient("MyApi", client =>
{
client.BaseAddress = new Uri("https://api.example.com/");
client.Timeout = TimeSpan.FromSeconds(30);
});
■ 2-2. コンストラクタインジェクションで受け取る
public class MyApiClient
{
private readonly HttpClient _http;
public MyApiClient(IHttpClientFactory factory)
{
_http = factory.CreateClient("MyApi");
}
}
3. HttpClientでの基本的なAPI呼び出し
■ 3-1. GET(JSON取得)
public record UserDto(int Id, string Name);
public async Task<UserDto?> GetUserAsync(int id)
{
var res = await _http.GetAsync($"/users/{id}");
if (!res.IsSuccessStatusCode)
return null;
var json = await res.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<UserDto>(json);
}
■ 3-2. POST(JSON送信)
public record CreateUserRequest(string Name);
public record CreateUserResponse(int Id, string Name);
public async Task<CreateUserResponse?> CreateUserAsync(string name)
{
var req = new CreateUserRequest(name);
var json = JsonSerializer.Serialize(req);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
var res = await _http.PostAsync("/users", content);
if (!res.IsSuccessStatusCode)
return null;
var resJson = await res.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<CreateUserResponse>(resJson);
}
■ 3-3. PUT / DELETE
public Task<HttpResponseMessage> UpdateUserAsync(int id, CreateUserRequest req)
{
var json = JsonSerializer.Serialize(req);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
return _http.PutAsync($"/users/{id}", content);
}
public Task<HttpResponseMessage> DeleteUserAsync(int id)
{
return _http.DeleteAsync($"/users/{id}");
}
4. エラー処理・タイムアウト・リトライ
業務アプリでは、ネットワークエラー・タイムアウト・5xx をどう扱うかが重要です。
■ 4-1. ステータスコードのチェック
var res = await _http.GetAsync("/users/1");
if (res.StatusCode == HttpStatusCode.NotFound)
{
// 見つからない → null扱いなど
}
res.EnsureSuccessStatusCode(); // 2xx以外なら例外
■ 4-2. タイムアウト
_http.Timeout = TimeSpan.FromSeconds(10);
「いつまでも待つ」はNG。業務要件に合わせて秒数を決めます。
■ 4-3. Pollyでリトライ(よくあるパターン)
builder.Services.AddHttpClient("MyApi", client =>
{
client.BaseAddress = new Uri("https://api.example.com/");
})
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, retry => TimeSpan.FromSeconds(Math.Pow(2, retry))));
一時的なネットワークエラーに対して、自動リトライが効くようになります。
5. Refitでインターフェイス駆動のAPIクライアント
Refitは、「インターフェイスに属性を書くと、実装を自動生成してくれる」 ライブラリです。 Retrofit(Android)のC#版のようなイメージです。
■ 5-1. インターフェイス定義
using Refit;
public record UserDto(int Id, string Name);
public record CreateUserRequest(string Name);
public record CreateUserResponse(int Id, string Name);
public interface IMyApi
{
[Get("/users/{id}")]
Task<UserDto> GetUserAsync(int id);
[Post("/users")]
Task<CreateUserResponse> CreateUserAsync([Body] CreateUserRequest request);
[Put("/users/{id}")]
Task UpdateUserAsync(int id, [Body] CreateUserRequest request);
[Delete("/users/{id}")]
Task DeleteUserAsync(int id);
}
■ 5-2. クライアント生成
var api = RestService.For<IMyApi>("https://api.example.com");
var user = await api.GetUserAsync(1);
■ 5-3. DI登録(HttpClientFactory連携)
builder.Services
.AddRefitClient<IMyApi>()
.ConfigureHttpClient(c =>
{
c.BaseAddress = new Uri("https://api.example.com");
c.Timeout = TimeSpan.FromSeconds(30);
});
あとはコンストラクタで IMyApi を受け取るだけで使えます。
6. 認証付きAPI(Bearerトークンなど)
■ 6-1. HttpClientでAuthorizationヘッダー
_http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
■ 6-2. Refit + HttpClientHandler
builder.Services
.AddRefitClient<IMyApi>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"))
.AddHttpMessageHandler(() => new AuthHandler());
public class AuthHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// ここでトークン取得・更新など
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token");
return await base.SendAsync(request, cancellationToken);
}
}
共通の認証ロジックをハンドラに閉じ込められます。
7. 業務アプリ向けAPIクライアント設計パターン
- HttpClientは必ずHttpClientFactory経由で使う
- APIごとにクライアントクラス or Refitインターフェイスを定義
- DTO(Request/Response)を明示的に定義する
- タイムアウト・リトライ・ログをポリシーとして共通化
- UI層からは「サービスインターフェイス」越しにAPIを呼ぶ
8. UIとの連携(WPF / Blazor / WinForms)
UIから直接HttpClientを叩くのではなく、 「アプリケーションサービス → APIクライアント」 の順で呼ぶと設計がきれいになります。
■ 8-1. アプリケーションサービス例
public interface IUserService
{
Task<UserDto?> GetUserAsync(int id);
}
public class UserService : IUserService
{
private readonly IMyApi _api;
public UserService(IMyApi api) => _api = api;
public Task<UserDto?> GetUserAsync(int id)
=> _api.GetUserAsync(id);
}
ViewModel / UI は IUserService だけを知っていればよくなります。
9. 業務アプリ向けベストプラクティス
- HttpClientはnewしない → HttpClientFactoryで管理
- タイムアウト・リトライ・ログを必ず設計する
- DTOを定義して「string JSON」のまま扱わない
- Refitでインターフェイス駆動にするとテストしやすい
- 認証・共通ヘッダーはDelegatingHandlerにまとめる
- UI層からはサービス経由でAPIを呼ぶ(直接HttpClientを触らない)
まとめ:HttpClientを理解したうえでRefitを使うと“API連携が一気に楽になる”
- HttpClient → 基本と仕組みを理解するための土台
- Refit → 実務での生産性を一気に上げるツール
- DI・ポリシー・DTO設計をセットで考えると、長期運用に耐えるAPIクライアントになる
「とりあえずHttpClientで叩く」 から一歩進んで、 「設計されたAPIクライアント」 を持つことで、 業務アプリの保守性と信頼性は大きく向上します。 この記事をベースに、あなたのプロジェクトに合ったAPI連携の形を設計してみてください。