CI4 不自动解析 Accept 头做内容协商,需手动读取并调用 setContentType() 和 setJSON()/setXML();API 版本应通过 URL 路径分组(如 /api/v1)配合独立命名空间实现;验证逻辑可用 Filter 复用,但业务与响应结构须按版本隔离。

Content-Type 和 Accept 头没被框架识别?
CI4 默认不自动解析 Accept 头做内容协商,也不会根据请求头自动切换 JSON/XML 响应格式——它只按你写的 return $this->response->setJSON() 或 setXML() 来发。想靠浏览器加 Accept: application/xml 就返回 XML?不行,得自己写判断逻辑。
常见错误现象:Postman 发 Accept: application/json,后端却返回 HTML 错误页;或 API 文档说支持多格式,实际只返回 JSON,没人告诉你漏写了协商代码。
- 先在控制器里手动读取:
$accept = $this->request->getHeaderLine('Accept'); - 用
stripos($accept, 'application/json')或正则匹配主流 MIME 类型(注意带q=权重的要拆解) - 别直接
header('Content-Type: ...')—— 用$this->response->setContentType(),否则和 CI4 的响应生命周期冲突 - 默认 fallback 格式必须明确指定,比如没匹配到就走 JSON,避免空响应
API 版本怎么加进路由不破坏现有结构?
把版本号塞进 URL 路径(如 /api/v1/users)最稳妥,CI4 的路由系统对这种前缀支持直接、无歧义。别用查询参数(?v=2)或请求头(Api-Version: 2),CI4 没内置解析器,后期维护成本高。
实操建议:
- 在
app/Config/Routes.php里分组定义:$routes->group('api/v1', ['namespace' => 'App\Controllers\V1'], function ($routes) { $routes->get('users', 'Users::index'); }); - 每个版本对应独立命名空间(
App\Controllers\V1、App\Controllers\V2),模型和服务类也按版本隔离,避免混用 - 别在同一个控制器里用
if ($version === 'v2')分支——版本升级时会变成意大利面条代码 - 旧版本路由可加重定向:
$routes->get('api/v1/users', 'V1\Users::index', ['filter' => 'cors']);,新版本另起一组
如何让 v1 和 v2 共享验证逻辑但不共享业务逻辑?
验证规则(比如 token 解析、权限检查)可以复用,但数据处理、字段映射、响应结构必须按版本隔离。CI4 的 Filter 机制是共享验证的自然选择,但别把业务逻辑塞进 Filter。
典型做法:
- 写一个
App\Filters\ApiAuth,在before()里解析 Bearer Token 并设置$this->request->setVar('user_id', $uid) - 在
app/Config/Filters.php中注册:'api_auth' => ['before' => ['api/v1/*', 'api/v2/*']] - v1 和 v2 的控制器各自实现
index(),但都从$this->request->getVar('user_id')取用户 ID——验证已做完,业务层不耦合 - 别在 Filter 里调用模型或构造响应体,那属于控制器职责;Filter 只该做“能不能过”,不该管“过完干啥”
响应格式不一致导致前端报错?检查这三点
同一 API 在不同版本或不同 Accept 头下返回结构不一致,是前端最常踩的坑。CI4 不强制规范响应体,全靠你写。最容易忽略的是状态码、错误格式、空数据处理。
- 成功响应统一用
200(列表)、201(创建)、204(删除),别混用200和202 - 错误响应必须带
status字段和机器可读的code(如"code": "VALIDATION_FAILED"),别只返回{"message": "xxx"} - 空数组要显式返回
[],别返回null或空字符串——JSON 解析会崩 - 用
$this->response->setStatusCode()显式设状态码,别依赖默认值;用setJSON()时传数组,别传对象(PHP 对象转 JSON 会丢键名)
版本迭代不是加个路由前缀就完事。真正的难点在于:v1 的某个字段在 v2 里改名了,但数据库字段没动,中间的 DTO 层你得手写两套;或者 v2 新增了分页参数,但 v1 的客户端还在传旧参数,兼容性兜底逻辑得清清楚楚写在控制器里——这些没法靠框架自动解决。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











