Tutorial/C# 与 .NET

在 C# 与 .NET 中使用汇率

使用 IHttpClientFactory、decimal、取消令牌、有限超时与来源感知处理构建带类型的 .NET 汇率客户端。

MMexchangerate.dev·Jul 17, 2026·6 分钟阅读

为 exchangerate.dev 注册一个带类型的 HttpClient,从配置注入 API 密钥,并将汇率反序列化为 decimal。传递取消令牌并设置有限超时,避免上游慢请求长期占用应用请求。

Key points
使用 IHttpClientFactory 统一管理连接与客户端配置。
把汇率反序列化为 decimal 并保留新鲜度字段。
传递 CancellationToken 并设置超时。
解码前调用 EnsureSuccessStatusCode()

注册带类型的 HttpClient

注册配置基础 URL、bearer token 与超时。带类型的客户端负责响应契约,并把调用方的取消传给网络请求。

csharp · Program.cs and FxClient.cscopy
// 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");
    }
}

从请求传递取消令牌

csharp · endpoint usagecopy
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"]。保留 SourceMarketSessionDataUpdatedAt,调用方才能区分实时交易日更新、每日参考值与闭市值。

重试策略必须收窄
短暂网络故障或部分 5xx 可以小幅退避后重试。不要把 401 当成短暂故障,也不要在 429 后制造重试风暴。
  • 保留一个带类型的客户端注册。
  • 汇率与金额使用 decimal。
  • 在完整调用链中传递取消。
  • 反序列化前拒绝非成功响应。
MM
exchangerate.dev
面向使用外汇数据的开发者的集成指南。

Keep reading

Guide一次获取历史外汇时间序列Read Reference读取 source 与 market_sessionRead Guide指示性汇率与可执行汇率Read