应使用 request->accept() 获取客户端首选 mime 类型,按权重排序取第一个;若为空则 fallback 默认格式;用 response()->type() 统一设类型并避免 header 与内容不一致。

怎么判断客户端要的是 JSON 还是 XML
ThinkPHP 默认只返回 JSON,但有些老系统或 IoT 设备会发 Accept: application/xml,这时候硬塞 JSON 会导致对方解析失败。别依赖 $_SERVER['HTTP_ACCEPT'] 手动 parse,TP 自带的 request->accept() 更可靠。
-
request->accept()返回的是一个按权重排序的 MIME 类型数组,比如['application/json', 'text/html'],第一个就是客户端最想要的 - 注意:某些安卓 WebView 或 Postman 会发
Accept: */*,这时request->accept()可能返回空数组,得 fallback 到默认格式 - 别用
strpos($_SERVER['HTTP_ACCEPT'], 'json')这种粗暴匹配——application/vnd.api+json会被漏掉
统一响应结构但不破坏原生 Content-Type
很多项目强行在控制器里写 header('Content-Type: application/json; charset=utf-8'),结果和 TP 的自动协商冲突,导致 XML 请求也返回 JSON 内容但 Header 还是 XML,客户端直接报错。
- 用
response()->type()设置类型,它会同步更新 Header 和序列化逻辑,比如response()->type('xml')->json($data)是错的,应该用response()->xml($data) - 如果要统一加
data包裹层,别在每个接口里重复写['code' => 0, 'data' => $xxx],改用think\Response的withData()方法或中间件拦截Response实例 - XML 响应对中文字符敏感,确保
$data里的字符串是 UTF-8 编码,否则response()->xml()会静默失败并返回空内容
Accept 头被 Nginx / CDN 缓存污染怎么办
上线后发现部分请求无论怎么改 Header 都返回 JSON,大概率是反向代理把 Accept 给吃掉了或者缓存了固定值。
- 检查 Nginx 配置里有没有
proxy_set_header Accept "application/json";这类硬编码——删掉,让原始头透传 - CDN(如阿里云、Cloudflare)默认不缓存带
Accept的请求,但如果开了「忽略特定 Header」或「强制缓存」,Accept就不会参与缓存 key 计算,导致不同格式请求命中同一个缓存体 - 临时验证方法:在入口
public/index.php开头加var_dump($_SERVER['HTTP_ACCEPT'] ?? 'MISSING');,看是否真能收到客户端原始值
自定义格式支持(比如 YAML、MsgPack)
TP 原生不支持 YAML,但不用重写整个响应流程,只要注册一个自定义类型就能接入。
- 在
app/common.php或服务提供者里调用think\Response::mimeType('yaml', 'application/x-yaml') - 然后实现一个
YamlResponse类继承think\Response,重写output()方法,用spyc或symfony/yaml序列化 - 关键点:必须在
output()开头调用$this->contentType($this->contentType)显式设置 Header,否则 TP 会按默认 JSON 类型设 Header - MsgPack 等二进制格式要额外设置
response()->sendHeader('Content-Transfer-Encoding', 'binary'),不然某些 HTTP 客户端会尝试 UTF-8 解码
真正麻烦的不是加新格式,而是当多个格式共存时,Accept 权重计算、fallback 顺序、错误响应格式一致性——这些地方没测全,线上就容易出现 200 状态但内容无法解析的情况。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











