HttpClient 必须复用,推荐用 AddHttpClient() 注册或声明为 static readonly;GET 参数需用 QueryHelpers 避免编码错误;POST JSON 要设 application/json;Bearer Token 通过 DefaultRequestHeaders 添加;异步方法必须 await,禁用 .Result/.Wait()。

HttpClient 必须复用,不能每次 new
频繁创建 HttpClient 实例会导致端口耗尽、DNS 缓存失效、连接无法复用,最终引发 SocketException 或响应超时。.NET Core 2.1+ 推荐用 IServiceCollection.AddHttpClient() 注册为单例或作用域服务;控制台或简单脚本中应声明为 static readonly 字段。
- 错误写法:
var client = new HttpClient();放在方法内 —— 每次调用都新建 - 正确写法(静态复用):
private static readonly HttpClient _client = new HttpClient(); - 注意:不要手动调用
_client.Dispose(),除非你确定生命周期结束且不再使用
GET 请求带参数要用 Uri.EscapeDataString 或 QueryHelpers
直接拼接 URL 容易因特殊字符(如空格、中文、&、=)导致 400 错误或服务端解析异常。不要手写 ?key=value&key2=value2,尤其当 value 来自用户输入或数据库时。
- 推荐方式:用
UriBuilder+QueryHelpers(需引用Microsoft.AspNetCore.WebUtilities) - 示例:
var uri = new UriBuilder("https://api.example.com/users"); uri.Query = QueryHelpers.AddQueryString("", new Dictionary<string, string> { ["name"] = "张三", ["page"] = "1" }); var response = await _client.GetAsync(uri.ToString()); - 不推荐:
$"https://...?name={name}"—— 中文会变成乱码,&可能破坏 query 结构
POST JSON 数据必须设 Content-Type 并用 StringContent
很多初学者用 client.PostAsync(url, new StringContent(json, Encoding.UTF8)) 却忘了设置 application/json,结果服务端收到 raw body 但解析失败,返回 415 Unsupported Media Type。
- 正确写法:
var json = JsonSerializer.Serialize(new { username = "alice", pwd = "123" }); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _client.PostAsync("https://api.example.com/login", content); - 别用
FormUrlEncodedContent发 JSON —— 它会把整个 JSON 当作一个字段值编码,服务端收不到对象结构 - 如果 API 要求 Bearer Token,记得加请求头:
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
异步方法必须 await,别用 .Result 或 .Wait()
在 ASP.NET Core 请求处理中调用 task.Result 极易引发线程死锁,尤其在有同步上下文的环境(如 MVC Controller)。HttpClient 的所有核心方法(GetAsync、PostAsync 等)都是 true async,必须用 await 驱动。
- 绝对避免:
var res = client.GetAsync(url).Result;或.Wait() - 控制器中必须标记
async Task<iactionresult></iactionresult>,方法体用await - 控制台主函数可用
await MainAsync().ConfigureAwait(false)配合static async Task Main(string[] args)(C# 7.1+)
真正容易出问题的不是“怎么发”,而是连接复用策略、编码安全性和异步流控制——这三个点没对齐,接口看似能通,上线后却间歇性失败或内存持续上涨。


















