exchangerate.dev用の型付きHttpClientを登録し、設定からAPIキーを注入し、レートをdecimalへ変換する。キャンセルとタイムアウトを明示して、遅い上流呼び出しがアプリケーション要求を長時間保持しないようにする。
要点
接続再利用、DNS更新の反映、ログ、一元設定に
IHttpClientFactoryを使う。レートを
decimalへ変換し、鮮度フィールドも保持する。すべての呼び出しに
CancellationTokenを渡し、タイムアウトを設定する。デコード前に
EnsureSuccessStatusCode()を呼ぶ。型付きHttpClientを登録する
登録がベースURL、bearer token、タイムアウトを管理する。型付きクライアントは応答契約を持ち、呼び出し元のキャンセルをネットワーク要求へ渡す。
// Program.cs
using System.Net.Http.Headers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient<FxClient>(client =>
{
var key = builder.Configuration["EXCHANGERATE_API_KEY"]
?? throw new InvalidOperationException("EXCHANGERATE_API_KEY is missing");
client.BaseAddress = new Uri("https://api.exchangerate.dev/v1/");
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", key);
client.Timeout = TimeSpan.FromSeconds(5);
});
// FxClient.cs
using System.Net.Http.Json;
using System.Text.Json.Serialization;
public sealed record LatestRates(
string Base,
string Source,
[property: JsonPropertyName("market_session")] string MarketSession,
[property: JsonPropertyName("data_updated_at")] DateTimeOffset DataUpdatedAt,
Dictionary<string, decimal> Rates
);
public sealed class FxClient(HttpClient http)
{
public async Task<LatestRates> GetLatestAsync(
string baseCurrency,
CancellationToken cancellationToken)
{
using var response = await http.GetAsync(
$"latest/{baseCurrency}?symbols=EUR,GBP",
cancellationToken);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<LatestRates>(
cancellationToken: cancellationToken)
?? throw new InvalidOperationException("Empty FX response");
}
}要求からキャンセルを渡す
var app = builder.Build();
app.MapGet("/fx", async (
FxClient fx,
CancellationToken cancellationToken) =>
{
var rates = await fx.GetLatestAsync("USD", cancellationToken);
return Results.Ok(new {
eur = rates.Rates["EUR"],
rates.Source,
rates.MarketSession,
rates.DataUpdatedAt,
});
});
app.Run();成功した観測だけをキャッシュする
共有キャッシュにはRates["EUR"]だけでなく完全な観測を保存する。Source、MarketSession、DataUpdatedAtがあれば、取引日のライブ更新と日次・閉場値を区別できる。
再試行の対象を絞る
短いネットワーク障害や選択した5xxだけを小さなバックオフで再試行する。401を一時障害として扱わず、429後に再試行の集中を作らない。
- 型付きクライアント登録を1つにする。
- レートと金額にdecimalを使う。
- 呼び出し全体にキャンセルを渡す。
- 非成功応答を変換前に拒否する。
ER
exchangerate.dev
FXデータを扱う開発者向けの統合ガイド。
