HttpClient 开启 Trace 后,TryAddWithoutValidation 写入的请求头变了
IHttpClientFactory 的 Trace 日志枚举头时触发延迟解析,可能改变 Accept 表示。限定 named client 日志,核对线上字节与包版本。
同一个请求,普通日志级别下发送 Accept: application/vnd.example+json;version=1,开启 Trace 后却变成 application/vnd.example+json; version=1。若客户端来自 IHttpClientFactory,应检查日志代码是否触发了已知请求头的延迟解析。
.NET 10 的 Microsoft.Extensions.Http 存在这条路径:TryAddWithoutValidation 先保存原始值,Trace 日志枚举普通 HttpHeaders 时会把已知头解析为结构化值,后续序列化再按规范格式输出。分号后的空格因此可能发生变化。
这不等于上述 Accept 在 HTTP 语义上必然不同,也不能只凭空格变化就断言服务端拒绝。要先确认业务是否真的依赖原始表示,以及具体哪个环节改变了它。
用相同调用链比较线上字节
保留同一 named client、相同 handler 顺序、相同头和值,只切换对应日志类别的 Trace 开关,在受控接收端比较实际收到的头。不要用普通 request.Headers 枚举来打印“发送前原值”:这个诊断动作本身也可能触发解析。
读取原始表示时可使用 request.Headers.NonValidated,但它不能撤销此前已经发生的解析。真实接收端结果仍然重要;代理、协议版本和服务器也可能规范化头的表示,客户端不能承诺整个链路逐字保留任意写法。
即使日志最终把头值显示成星号,日志状态在格式化或枚举时也可能已经访问 Headers。脱敏保护内容,和避免解析副作用是两件事。不要为了看清差异关闭所有凭据脱敏。
只关闭这个客户端的 Trace 头日志
如果当前确实需要避免这条日志路径,可先针对 named client 的标准日志类别降到 Debug 或更高。下面假设客户端注册名为 partner,合并到已有应用配置,不覆盖其他日志项:
{
"Logging": {
"LogLevel": {
"System.Net.Http.HttpClient.partner.LogicalHandler": "Debug",
"System.Net.Http.HttpClient.partner.ClientHandler": "Debug"
}
}
}
标准工厂日志在 Trace 才记录这些头。配置提供器、额外日志 provider 或自定义 logger 有更具体规则时,需要检查最终生效值。两类 handler 都要核对,不能只关闭其中一个就认为再也不会访问请求头。
也可以对确需隔离的客户端使用正常注册接口:
services.AddHttpClient("partner").RemoveAllLoggers();
这段放在现有依赖注入配置中,并要求所用 Microsoft.Extensions.Http 提供该扩展。它会移除这个 builder 的工厂日志,损失比调低 Trace 更大;它不移除所有自定义 DelegatingHandler,也不保证别处不再枚举头。保留必要的请求结果和失败诊断,避免为了头格式完全失去可观测性。
主分支的日志实现已改用 NonValidated 枚举,但正式 10.0.12 的对应实现仍枚举普通 Headers。升级时核对实际包版本与目标正式实现,不把运行时升级或主分支提交直接当成所有 Microsoft.Extensions.Http 包已修复。
处理后比较同一受控请求在实际接收端的表示与业务结果,再检查错误日志、耗时记录和其他客户端的日志仍符合要求。若系统原本要求对头进行规范化后再校验,应保留那份契约,不能把“原始空格不变”当作所有 HTTP 客户端的通用保证。


