最稳妥可靠的方式是将版本号放在URL路径中,如/api/v1/users;因其直观、易调试、兼容CDN与日志系统,且主流PHP框架原生支持,而Accept头和查询参数方式存在配置易漏、破坏REST语义、缓存不友好等缺陷。

PHP平台设计RESTful接口版本号,最稳妥可靠的方式是把版本号放在URL路径里,比如 /api/v1/users 或 /api/v2/orders。这不是权宜之计,而是工程实践中被反复验证的主流选择——它直观、可调试、易缓存、兼容CDN和日志系统,且绝大多数PHP框架(Laravel、Slim、Yii2、Lumen)原生支持这种模式。
为什么首选路径版本(/v1/)
虽然RFC允许用 Accept 头(如 Accept: application/vnd.myapp.v2+json)或查询参数(?version=v2)传版本,但它们在PHP生态中存在明显短板:
- Accept头需手动挂载ContentNegotiator并清空
formats配置,否则Yii/Laravel等默认忽略,容易漏配失效 - 查询参数方式不规范,破坏REST资源语义,SEO不友好,且无法被CDN或反向代理按路径缓存
- 路径版本直接体现在URL中,curl、Postman、浏览器地址栏一目了然,排查问题时无需抓包看header
- 运维工具(Nginx日志、APM监控、API网关路由策略)天然按路径做分流、限流、灰度,无需额外解析header
路径版本怎么组织才不混乱
关键不是“加个v1”,而是建立物理隔离与清晰边界:
- 每个版本对应一个独立模块:如
app/modules/v1/和app/modules/v2/,各自有完整的Module.php、controllers/、models/ - 命名空间严格分离:
v1\Module的控制器必须用app\modules\v1\controllers,不能复用全局app\controllers - 路由规则用正则透传,避免为每个接口硬写多条rule。例如在Yii2中配置:
'api/<v:>/<controller:>' => '<v>/<controller>/index'</controller></v></controller:></v:> - 模块注册完整声明:
'modules' => ['v1' => ['class' => 'app\modules\v1\Module'], 'v2' => ['class' => 'app\modules\v2\Module']]
版本升级时要注意什么
版本不是简单复制粘贴改个数字,核心是控制耦合与演进节奏:
立即学习“PHP免费学习笔记(深入)”;
- 禁止跨版本共享Model或Serializer——v2的User模型不能直接调用v1的UserRepository
- v2新增字段或修改逻辑时,v1接口行为必须冻结,不得悄悄变更返回结构
- 废弃旧版前,至少保留一个大版本周期,并在响应Header中添加
Deprecated: true或文档明确标注 - 数据库迁移需兼容双版本:v1仍读旧字段,v2可读新字段,避免一次DDL导致v1接口崩塌
不复杂但容易忽略。



















