高级用法
本指南涵盖 RestClient.Net 生产环境使用的高级模式。
使用 IHttpClientFactory
在生产环境中始终使用 IHttpClientFactory 以避免套接字耗尽:
// Program.cs
builder.Services.AddHttpClient("api", client =>
{
client.BaseAddress = new Uri("https://api.example.com");
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.Timeout = TimeSpan.FromSeconds(30);
});
// 在服务中
public class UserService(IHttpClientFactory httpClientFactory)
{
public async Task<Result<User, HttpError<ApiError>>> GetUserAsync(
string userId,
CancellationToken ct = default)
{
var client = httpClientFactory.CreateClient("api");
return await client.GetAsync(
url: $"/users/{userId}".ToAbsoluteUrl(),
deserializeSuccess: Deserializers.Json<User>,
deserializeError: Deserializers.Error,
cancellationToken: ct
);
}
}
类型化客户端
创建强类型客户端以便更好地组织代码:
// UserApiClient.cs
public class UserApiClient(HttpClient httpClient)
{
public Task<Result<User, HttpError<ApiError>>> GetUserAsync(
string userId,
CancellationToken ct = default) =>
httpClient.GetAsync(
url: $"/users/{userId}".ToAbsoluteUrl(),
deserializeSuccess: Deserializers.Json<User>,
deserializeError: Deserializers.Error,
cancellationToken: ct
);
public Task<Result<User, HttpError<ApiError>>> CreateUserAsync(
CreateUserRequest request,
CancellationToken ct = default) =>
httpClient.PostAsync(
url: "/users".ToAbsoluteUrl(),
body: request,
serializeRequest: body => JsonContent.Create(body),
deserializeSuccess: Deserializers.Json<User>,
deserializeError: Deserializers.Error,
cancellationToken: ct
);
}
// Program.cs
builder.Services.AddHttpClient<UserApiClient>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
});
使用 Polly 的重试策略
为瞬态故障添加重试逻辑:
using Microsoft.Extensions.Http.Resilience;
// Program.cs
builder.Services.AddHttpClient("api")
.AddStandardResilienceHandler(options =>
{
options.Retry.MaxRetryAttempts = 3;
options.Retry.Delay = TimeSpan.FromMilliseconds(500);
options.Retry.UseJitter = true;
options.Retry.ShouldHandle = args => ValueTask.FromResult(
args.Outcome.Exception is not null ||
args.Outcome.Result?.StatusCode >= HttpStatusCode.InternalServerError
);
});
自定义重试策略
需要更多控制时:
builder.Services.AddHttpClient("api")
.AddPolicyHandler(
HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == HttpStatusCode.TooManyRequests)
.WaitAndRetryAsync(
retryCount: 3,
sleepDurationProvider: (retryAttempt, response, context) =>
{
// 检查 Retry-After 头
if (response.Result?.Headers.RetryAfter?.Delta is { } delta)
{
return delta;
}
// 指数退避
return TimeSpan.FromSeconds(Math.Pow(2, retryAttempt));
},
onRetryAsync: (outcome, timespan, retryAttempt, context) =>
{
Console.WriteLine($"第 {retryAttempt} 次重试,等待 {timespan}");
return Task.CompletedTask;
}
)
);
身份验证处理器
创建用于身份验证的委托处理器:
public class AuthenticationHandler(ITokenService tokenService) : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var token = await tokenService.GetAccessTokenAsync(cancellationToken);
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
return await base.SendAsync(request, cancellationToken);
}
}
// Program.cs
builder.Services.AddTransient<AuthenticationHandler>();
builder.Services.AddHttpClient("api")
.AddHttpMessageHandler<AuthenticationHandler>();
令牌刷新处理器
自动刷新过期令牌:
public class TokenRefreshHandler(
ITokenService tokenService,
ILogger<TokenRefreshHandler> logger) : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var token = await tokenService.GetAccessTokenAsync(cancellationToken);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
logger.LogInformation("令牌已过期,正在刷新...");
token = await tokenService.RefreshTokenAsync(cancellationToken);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
response = await base.SendAsync(request, cancellationToken);
}
return response;
}
}
请求/响应日志
记录所有 HTTP 流量:
public class LoggingHandler(ILogger<LoggingHandler> logger) : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var requestId = Guid.NewGuid().ToString("N")[..8];
logger.LogInformation(
"[{RequestId}] {Method} {Uri}",
requestId,
request.Method,
request.RequestUri
);
var stopwatch = Stopwatch.StartNew();
var response = await base.SendAsync(request, cancellationToken);
stopwatch.Stop();
logger.LogInformation(
"[{RequestId}] {StatusCode} 耗时 {ElapsedMs}ms",
requestId,
(int)response.StatusCode,
stopwatch.ElapsedMilliseconds
);
return response;
}
}
断路器
防止级联故障:
builder.Services.AddHttpClient("api")
.AddPolicyHandler(
HttpPolicyExtensions
.HandleTransientHttpError()
.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30),
onBreak: (result, duration) =>
{
Console.WriteLine($"断路器打开,持续 {duration}");
},
onReset: () =>
{
Console.WriteLine("断路器重置");
}
)
);
自定义序列化
使用不同的序列化器:
System.Text.Json 带选项
public static class Deserializers
{
private static readonly JsonSerializerOptions Options = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
public static async Task<T> Json<T>(HttpContent content, CancellationToken ct)
where T : class =>
await content.ReadFromJsonAsync<T>(Options, ct)
?? throw new InvalidOperationException($"反序列化 {typeof(T).Name} 失败");
}
Newtonsoft.Json
public static async Task<T> NewtonsoftJson<T>(HttpContent content, CancellationToken ct)
{
var json = await content.ReadAsStringAsync(ct);
return JsonConvert.DeserializeObject<T>(json)
?? throw new InvalidOperationException($"反序列化 {typeof(T).Name} 失败");
}
缓存响应
缓存成功的响应:
public class CachingService(
IMemoryCache cache,
IHttpClientFactory httpClientFactory)
{
public async Task<Result<User, HttpError<ApiError>>> GetUserAsync(
string userId,
CancellationToken ct = default)
{
var cacheKey = $"user:{userId}";
if (cache.TryGetValue<User>(cacheKey, out var cached))
{
return new Result<User, HttpError<ApiError>>.Ok(cached);
}
var client = httpClientFactory.CreateClient("api");
var result = await client.GetAsync(
url: $"/users/{userId}".ToAbsoluteUrl(),
deserializeSuccess: Deserializers.Json<User>,
deserializeError: Deserializers.Error,
cancellationToken: ct
);
if (result is Result<User, HttpError<ApiError>>.Ok(var user))
{
cache.Set(cacheKey, user, TimeSpan.FromMinutes(5));
}
return result;
}
}
请求关联
为分布式追踪添加关联 ID:
public class CorrelationHandler(IHttpContextAccessor httpContextAccessor) : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var correlationId = httpContextAccessor.HttpContext?
.Request.Headers["X-Correlation-ID"].FirstOrDefault()
?? Guid.NewGuid().ToString();
request.Headers.Add("X-Correlation-ID", correlationId);
return base.SendAsync(request, cancellationToken);
}
}
测试
模拟 HttpClient
public class UserServiceTests
{
[Fact]
public async Task GetUser_ReturnsUser_WhenFound()
{
// 准备
var handler = new MockHttpMessageHandler(request =>
new HttpResponseMessage(HttpStatusCode.OK)
{
Content = JsonContent.Create(new User("1", "张三"))
});
var client = new HttpClient(handler)
{
BaseAddress = new Uri("https://api.example.com")
};
var service = new UserService(client);
// 执行
var result = await service.GetUserAsync("1");
// 断言
Assert.IsType<OkUser>(result);
}
}
最佳实践总结
- 在生产环境中始终使用 IHttpClientFactory
- 明确配置超时
- 为瞬态错误添加重试策略
- 使用断路器防止级联故障
- 实现身份验证处理器 进行令牌管理
- 记录请求和响应日志 用于调试
- 添加关联 ID 用于分布式追踪
- 在适当时缓存昂贵的操作
- 使用模拟处理器测试 以确保可靠性
下一步
- OpenAPI 生成器 - 自动生成客户端
- MCP 服务器 - Claude Code 集成
- API 参考 - 完整文档