目标
406 表示服务器理解了请求,但拒绝以你提供的任何媒体类型返回表示。复合 Accept 头里混着多个类型和参数时,没人知道到底是哪一项触怒了服务器。本食谱把 Accept 头逐项拆开做二分实验,让每个媒体类型单独投票,把猜测变成一行明确的结论,并据此判断该修客户端还是修服务端。
步骤要点
- 先用最简单的 Accept: application/json 建立基线,返回 200 即排除路由与鉴权问题,把范围收窄到协商逻辑。
- 把复合头拆成多个候选媒体类型(含带参数与不带参数的变体),逐个请求并打印状态码,通常恰好一项返回 406。
- 用 HEAD 请求查看服务器真实提供的 Content-Type 与是否有 Vary: Accept,区分“有意的协商”与“中间件误伤”。
- 根据契约归属修复:客户端不该要的就删掉该头;服务端承诺的就正确注册解析器(对比 MIME 类型时先比类型再比参数),修复后用原始复合头复测确认 200。
排错提示
每种类型单独都 200 但复合头 406,说明解析器处理不了列表参数,先拆请求或修解析器;只有过网关才 406 是网关改写或按更严格白名单校验 Accept,用旁路请求对比源站实际收到的头再对齐白名单;返回 405 而非 406 说明是方法而非媒体类型问题,服务器先校验方法再协商,先改动词。
适用边界
curl 只能证明今天这台服务器的行为,RFC 对协商留有实现自由度,网关升级后需跨版本复验;很多服务器忽略 q 质量值的排序,不要依赖它做按优先级的协商。
代码示例
for mt in "application/json" \
"application/json; charset=utf-8" \
"text/html" \
"application/vnd.api+json"; do
printf "%-40s -> " "$mt"
curl -s -o /dev/null -w "%{http_code}\n" \
"https://staging.example.com/api/report" -H "Accept: $mt"
done
# application/json -> 200
# application/json; charset=utf-8 -> 406 <-- parameter mishandled
# text/html -> 200
# application/vnd.api+json -> 406 <-- type never registered