httprequestmessage本身不发送请求,仅作为请求容器;必须交由httpclient.sendasync执行,否则仅为“空壳”,常见错误是构造后未调用sendasync导致静默卡住或objectdisposedexception。

HttpRequestMessage 本身不发送请求,必须配 HttpClient.SendAsync
HttpRequestMessage 只是一个请求容器,它不包含网络能力。直接 new 它出来、设置 RequestUri 和 Headers 后,如果不交给 HttpClient 执行,就只是个“空壳”。常见错误是写完 var req = new HttpRequestMessage(...) 就以为万事大吉,结果没调用 client.SendAsync(req),程序静默卡住或抛 ObjectDisposedException(因后续又用了已释放的 client)。
正确链路只有这一种:
- 创建
HttpClient实例(推荐复用,别每次 new) - 构造
HttpRequestMessage,填好Method、RequestUri、Content(如有)、Headers - 传给
client.SendAsync(req),await 等待响应 - 手动调用
response.EnsureSuccessStatusCode()检查状态码(HttpClient默认不抛异常)
POST 带 JSON Body 时,Content-Type 必须显式设为 application/json
用 HttpRequestMessage 发送 JSON 数据,不能只靠 StringContent 构造时传入 "application/json",还要确认 Content.Headers.ContentType 确实生效。某些旧版 .NET Framework 下,如果没设或拼错,服务器可能返回 415 Unsupported Media Type。
安全写法:
var json = JsonSerializer.Serialize(data);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// 强制覆盖 ContentType,避免被中间层篡改
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = content
};
注意:StringContent 构造函数第三个参数是 MIME 类型,但内部不一定严格绑定到 Headers.ContentType;显式赋值更可靠。
Authorization 头要加在 RequestMessage.Headers,不是 DefaultRequestHeaders
如果你用的是单次请求专用的 HttpRequestMessage(比如不同请求带不同 token),授权头必须加在该实例的 Headers 上,而不是 HttpClient.DefaultRequestHeaders。后者是全局默认头,一旦设置会污染所有后续请求——尤其在并发场景下极易导致 token 错乱。
正确做法:
- 需要 per-request 鉴权:用
request.Headers.Add("Authorization", "Bearer xxx") - 需要全局固定鉴权(如服务间调用统一 token):才设
client.DefaultRequestHeaders.Authorization - 混用时注意清除:若某次请求不需要 auth,得手动
request.Headers.Remove("Authorization"),否则继承了 client 的默认头
GET 请求带 Query 参数,别拼字符串,用 UriBuilder 或 QueryHelpers
手写 url + "?key=value&..." 容易出编码问题。中文、特殊符号(如 &、=、空格)不 encode 会导致 400 Bad Request 或参数截断。
推荐两种方式:
- .NET Core 2.1+:用
Microsoft.AspNetCore.WebUtilities.QueryHelpers(即使非 ASP.NET 项目也可 NuGet 引入Microsoft.AspNetCore.WebUtilities) - 全平台兼容:用
UriBuilder+HttpUtility.ParseQueryString
示例(QueryHelpers):
var query = new Dictionary<string string> { ["q"] = "c# http", ["page"] = "1" };
var uri = new Uri($"{baseUrl}?{QueryHelpers.AddQueryString("", query)}");
var request = new HttpRequestMessage(HttpMethod.Get, uri);
</string>
这个细节在调试时很难定位——请求发出去了,服务器收不到完整参数,日志里也看不出拼接痕迹。










