C# HTTP客户端:正确发起请求——高级水平
在现代.NET开发中,HTTP请求处理已远远超出简单的WebClient或基础HttpClient调用。在高级水平上,需要考虑连接池管理、正确的错误处理、超时、重试策略以及异步执行。本文将介绍C# HTTP客户端的最佳实践,帮助您创建可靠且高性能的应用程序。
1. 正确使用HttpClient与连接池
最常见的错误之一是为每个请求创建新的HttpClient实例。这会导致套接字耗尽(socket exhaustion),因为每个实例都会保持一个处于TIME_WAIT状态的开放TCP连接。
错误做法:
public async Task<string> GetDataAsync(string url){ using (var client = new HttpClient()) { return await client.GetStringAsync(url); }}正确做法:使用单个静态或单例HttpClient实例。
public class HttpService{ private static readonly HttpClient _client = new HttpClient();
public async Task<string> GetDataAsync(string url) { return await _client.GetStringAsync(url); }}如需更精细的控制,请使用Microsoft.Extensions.Http包中的IHttpClientFactory。该工厂管理连接池,并在DNS故障时自动恢复连接。
// 在 Startup.cs 或 Program.cs 中services.AddHttpClient("MyClient", client =>{ client.BaseAddress = new Uri("https://api.example.com"); client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");});
// 在服务中public class MyService{ private readonly IHttpClientFactory _httpClientFactory;
public MyService(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; }
public async Task<string> GetDataAsync() { var client = _httpClientFactory.CreateClient("MyClient"); return await client.GetStringAsync("/data"); }}2. 超时与请求取消
没有超时设置,您的客户端可能会无限期挂起。HttpClient提供两种机制:
- Timeout — 整个请求的全局超时。
- CancellationToken — 用于从外部取消请求(例如用户关闭应用程序)。
合理设置Timeout。对于大多数API,10–30秒是合适的。
var client = new HttpClient{ Timeout = TimeSpan.FromSeconds(15)};
// 使用CancellationTokenSource的示例using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));try{ var response = await client.GetAsync("https://slow-api.example.com", cts.Token);}catch (TaskCanceledException){ Console.WriteLine("请求因超时被取消。");}重要提示: Timeout和CancellationToken协同工作。如果CancellationToken先触发,请求将被更快取消。
3. 错误处理与重试策略
网络故障和服务器临时错误是常见情况。与其直接失败,不如实现带有指数退避的重试策略。Polly库非常适合此目的。
using Polly;using Polly.Extensions.Http;
// 定义重试策略var retryPolicy = HttpPolicyExtensions .HandleTransientHttpError() // 5xx, 408 .Or<HttpRequestException>() .WaitAndRetryAsync(3, retryAttempt => Tim