Yii2 RESTful接口版本控制核心是Module+UrlRule+ContentNegotiator三者协同:v1/v2须物理隔离模块、独立命名空间与配置,路由用正则透传版本,Accept头需启用ContentNegotiator并清空formats,media type须精确注册,版本解析统一收口beforeAction,model/serializer严禁跨版本共享。

Yii2 RESTful 接口版本控制不是靠加个参数或改个配置就能搞定的事,核心在于模块物理隔离 + 路由智能透传 + 内容协商精准生效。三者缺一不可,否则容易埋下维护隐患、测试失效、IDE跳转异常等问题。
模块必须物理隔离
每个版本(如 v1、v2)应作为独立模块存在,路径、命名空间、配置完全分离:
- 目录结构严格区分:
api/modules/v1/和api/modules/v2/各自包含Module.php、controllers/、models/ -
v1/Module.php中$controllerNamespace = 'api\modules\v1\controllers',不能写成'api\controllers'或共享命名空间 - 控制器类名可相同(如
UserController),但必须分别定义在各自模块下,避免逻辑混杂 -
init()方法内禁止加载其他版本的 service、model 或 config,防止隐式耦合
路由规则用正则捕获,不硬编码
URL 中的版本号应作为参数透传,而非为每个版本单独写一条 rule:
- 在
urlManager.rules中添加:'api/<v1>/<controller:>/<id:>' => '<version>/<controller>/view'</controller></version></id:></controller:></v1> - 更通用写法:
'api/<v>/<controller:>' => '<version>/<controller>/index'</controller></version></controller:></v> - 确保
enablePrettyUrl = true,否则/v1/users会被当成 pathinfo 处理失败 - 模块注册必须完整:
'modules' => ['v1' => ['class' => 'api\modules\v1\Module'], 'v2' => ['class' => 'api\modules\v2\Module']]
Accept 头必须显式启用并注册 media type
仅传 application/vnd.myapp.v2+json 是无效的,Yii 默认忽略它:
- 在控制器基类(如
BaseController)中挂载ContentNegotiator行为:'class' => 'yii\filters\ContentNegotiator', 'acceptHeader' => true, 'formats' => [] -
formats => []是关键,否则 Yii 优先按.json后缀匹配,跳过 Accept 解析 - 在应用配置(如
config/web.php)中注册 media type:'response' => ['formatters' => ['application/vnd.myapp.v2+json' => 'yii\web\JsonResponseFormatter']] - media type 字符串必须完全一致——
vnd.myapp.v2+json和vnd.myapp.v2+json; charset=UTF-8视为不同类型
版本解析统一收口,优先级明确
避免多处判断导致逻辑分散,应在 beforeAction() 中集中处理:
- 约定优先级:URL 路径版本 > Accept 头 > 默认版本(如 v1)
- 从
Yii::$app->request->get('version')或$this->module->params['version']获取路径版本 - 再读取
Yii::$app->request->getHeaders()->get('Accept')提取 media type 并比对 - 最终将解析出的版本号存入
$this->version或Yii::$app->params['apiVersion'],供后续 service、serializer 使用 - model 和 serializer 严禁跨版本复用,v2 的数据结构变更不应影响 v1 的响应格式


















