HttpClient是.NET Core/5+官方推荐的线程安全、支持连接池的HTTP客户端,应全局单例复用;POST JSON用StringContent设application/json,表单用FormUrlEncodedContent;自定义Header需提前设置,禁用.Result避免死锁。

用 HttpClient 发送 POST 请求,别再手写 HttpWebRequest
直接上 HttpClient,它是 .NET Core / .NET 5+ 官方推荐、线程安全、支持连接池的首选方式。手写 HttpWebRequest 不仅冗长,还容易漏关流、复用失败、超时不生效。
常见错误现象:HttpRequestException: Connection refused 或请求卡死 —— 很可能是没正确复用 HttpClient 实例(比如每次请求都 new 一个)。
- 全局只创建一次
HttpClient实例(建议注入为 Singleton) - 用
PostAsync+StringContent发 JSON,记得设MediaType为"application/json" - 务必 await,别忘了
EnsureSuccessStatusCode()检查 HTTP 状态码
var client = new HttpClient();
var json = JsonSerializer.Serialize(new { name = "test" });
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.example.com/users", content);
response.EnsureSuccessStatusCode(); // 抛出 4xx/5xx 异常
POST 表单数据(application/x-www-form-urlencoded)怎么发
不是所有 WebAPI 都接受 JSON;有些老接口只认表单键值对,比如登录接口或 ASP.NET MVC 后端。这时候不能传 JSON 字符串,得用 FormUrlEncodedContent。
容易踩的坑:把对象直接序列化成 JSON 再塞进 FormUrlEncodedContent —— 这会导致后端收不到任何字段,因为格式完全不匹配。
- 用
Dictionary<string, string>构建键值对 - 传中文时不用手动 URL 编码,
FormUrlEncodedContent会自动处理 - 如果后端要求空值字段也提交,字典里显式加
"",别跳过 key
var data = new Dictionary<string, string>
{
{ "username", "admin" },
{ "password", "123" }
};
var content = new FormUrlEncodedContent(data);
var response = await client.PostAsync("https://api.example.com/login", content);
如何模拟 Postman 的「Raw → JSON」和「Headers 手动加」
Postman 的 Raw JSON 模式本质就是带 Content-Type: application/json 的 POST;而自定义 Header(比如 Authorization: Bearer xxx)必须在 request 发出前设置,不能靠响应头反推。
性能影响:Header 太多或值太长不会显著拖慢,但 Authorization 值泄露(如硬编码在代码里)是严重安全风险。
- Header 必须调用
client.DefaultRequestHeaders.Add()或request.Headers.Add() - Bearer Token 建议从配置或密钥管理服务读取,别写死
- 如果某个请求要临时覆盖全局 Header(比如换 token),就别用
DefaultRequestHeaders,改用HttpRequestMessage实例
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.example.com/data");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "xxx-token");
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
同步调用(.Result)为什么总卡死或死锁
在 ASP.NET Core MVC 控制器或 WinForms/WPF UI 线程里直接调 PostAsync().Result,99% 会死锁 —— 因为 await 试图回调到被阻塞的上下文,而那个上下文正等着 .Result 返回。
兼容性影响:.NET Framework 下更敏感;.NET 6+ 默认禁用同步上下文,但 UI 框架(如 WPF)仍会恢复它。
- 永远用
await,不要.Result或.Wait() - Controller Action 必须标记
async Task<IActionResult>,不能是Task<IActionResult>少了 async - 实在无法异步(如第三方库强制同步入口),用
ConfigureAwait(false)降低风险,但只是缓解,不是解法
复杂点在于:死锁不报错,只卡住,调试时看不出异常,容易误判为网络问题或 API 故障。










