引言:为什么需要在 C# 中使用合适的 HTTP 客户端
在现代开发中,几乎没有任何应用程序可以不与外部服务交互而正常运行。无论是向服务器发送数据、获取货币汇率,还是与云 API 协作——所有这些都需要正确实现 HTTP 请求。在 C# (.NET) 生态系统中,完成此任务的主要工具是 HttpClient 类。然而,许多开发者,尤其是初学者,会犯一些典型错误:为每个请求创建新的 HttpClient 实例、忘记处理异常或异步操作使用不当。
本文将详细讲解如何正确组织 C# 中的 HTTP 客户端,介绍发送 GET 和 POST 请求、处理请求头和请求体,以及错误处理的最佳实践。阅读本文后,您将能够编写可靠且高性能的代码来与任何 REST API 进行交互。
C# 中 HttpClient 的基础知识
HttpClient 类是 System.Net.Http 命名空间的一部分,可在所有现代版本的 .NET(Core、5、6、7、8 及以上版本)中使用。其主要优势在于支持异步操作和自动连接池管理。
正确创建 HttpClient
最重要的规则:不要为每个请求创建新的 HttpClient。这可能导致套接字耗尽。相反,应为整个应用程序生命周期使用单个实例,或使用 IHttpClientFactory。
// ❌ 错误:为每个请求创建新的 HttpClientusing (var client = new HttpClient()){ var response = await client.GetAsync("https://api.example.com");}
// ✅ 正确:为所有请求使用同一个 HttpClientpublic class MyHttpService{ private static readonly HttpClient _client = new HttpClient(); public async Task<string> GetDataAsync(string url) { return await _client.GetStringAsync(url); }}基本配置
初始化 HttpClient 时,可以设置基础地址、超时时间和默认请求头:
public class ApiClient{ private readonly HttpClient _httpClient; public ApiClient() { _httpClient = new HttpClient { BaseAddress = new Uri("https://api.pythonlib.ru/"), Timeout = TimeSpan.FromSeconds(10) }; // 每次请求都会发送的请求头 _httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0"); }}发送 GET 和 POST 请求
让我们来看看两种最常见的 HTTP 请求类型:获取数据(GET)和发送数据(POST)。
GET 请求:获取数据
GET 请求用于从服务器读取数据。它不应更改资源状态,也不包含请求体。
public async Task<string> GetUserAsync(int userId){ try { var response = await _httpClient.GetAsync($"/api/users/{userId}"); response.EnsureSuccessStatusCode(); // 出错时会抛出异常 return await response.Content.ReadAsStringAsync(); } catch (HttpRequestException ex) { Console.WriteLine($"请求错误:{ex.Message}"); throw; }}对于简单情况,可以使用简写形式:
string json = await _httpClient.GetStringAsync("/api/users/42");POST 请求:发送数据
POST 请求用于创建新资源。数据通过请求体发送,通常采用 JSON 格式。
public async Task<User> CreateUserAsync(User newUser){ // 将对象序列化为 JSON var json = JsonSerializer.Serialize(newUser);