应使用 httpclient 的 postasync + formurlencodedcontent 而非手动拼接字符串,因其自动处理 uri 编码、null 值及 content-type;需复用 httpclient 实例并手动设置必要请求头。

直接用 HttpClient 的 PostAsync + FormUrlEncodedContent,别手写 application/x-www-form-urlencoded 字符串,否则中文乱码、特殊字符转义、空值处理全得自己兜底。
为什么不能直接拼接字符串发 POST?
手动拼 key1=value1&key2=value2 看似简单,但实际会踩三个坑:
-
value里有空格、中文、&、=时,不调用Uri.EscapeDataString就会出错或被服务端截断 -
null或空字符串字段要不要传?不同接口约定不同,手拼容易漏判 - HTTP 头没设
Content-Type: application/x-www-form-urlencoded,有些后端(尤其 .NET Core Web API)直接 415 Unsupported Media Type
FormUrlEncodedContent 怎么用才安全?
它内部自动做 URI 编码,也兼容 null 值(默认转成空字符串),但注意构造方式:
- 必须传
IEnumerable<keyvaluepair string>></keyvaluepair>,不是Dictionary(虽然Dictionary实现了该接口,但顺序不保证;表单提交顺序一般不影响,但某些老系统校验签名时会按 key 顺序算) - 推荐用数组或列表显式声明顺序:
var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string string>("username", "张三"), new KeyValuePair<string string>("token", "abc%2Fdef"), // 自动再编码一次,所以别提前 encode new KeyValuePair<string string>("remark", "") });</string></string></string> - 如果值来自用户输入,
FormUrlEncodedContent会把它当原始字符串处理,不会二次污染 —— 所以你传"a&b=c",它会变成a%26b%3Dc,而不是你手动拼错的a&b=c
HttpClient 实例要不要复用?
要。每次 new HttpClient 可能引发 socket 耗尽(SocketException: Too many open files),尤其在高并发场景:
- 全局只用一个静态
HttpClient实例(或用IHttpClientFactory注入) -
FormUrlEncodedContent是一次性对象,用完即弃,不用缓存 - 别把
HttpClient包进using—— 它不是设计来短生命周期使用的 - 示例片段:
private static readonly HttpClient _client = new HttpClient(); // ... var response = await _client.PostAsync("https://api.example.com/login", content);
真正容易被忽略的是:某些内网接口要求 User-Agent 头,或者对 Host 头敏感;FormUrlEncodedContent 不会帮你设这些,得手动加:_client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0")。











