用modules实现多版本路由隔离最稳妥:每个版本对应独立module,天然隔离命名空间、配置与权限;需显式声明controllerNamespace,BaseRestController中禁用session与auto-login但保留user组件;参数校验用ActionFilter统一处理,微信OAuth需改用cURL并严格校验access_token。

用 modules 实现多版本路由隔离最稳妥
Yii2 官方不内置 API 版本控制机制,靠 modules 是最轻量、最可控的方案——它天然隔离命名空间、配置、行为和权限,避免 v1/v2 接口互相污染。
- 每个版本对应一个 module,比如
'v1' => ['class' => 'api\modules\v1\Module'],路径自动映射为/v1/xxx - 模块内控制器可复用相同类名(如
UserController),但实际代码完全独立,改 v2 不影响 v1 - 别把版本塞进 controller ID(如
v1/user)或 action 名里,否则路由规则爆炸、行为继承混乱、Gii 生成失效 - 注意
controllerNamespace必须显式声明在Module.php中,否则会 fallback 到默认命名空间,导致 404
BaseRestController 要禁用 session 和 CSRF,但别动 user 组件初始化
REST API 是无状态的,Yii::$app->user 仍需工作(用于 token 验证),但 session 和 auto-login 必须关掉——否则请求可能因 session 启动失败而中断,尤其在 CLI 或无 cookie 场景下。
- 在基类
BaseRestController::init()中设Yii::$app->user->enableSession = false和Yii::$app->user->enableAutoLogin = false - 千万别写
Yii::$app->session = null或 unsetuser组件,否则$user->loginByAccessToken()会直接报错 - 如果某接口真不需要鉴权,就继承
yii\rest\Controller而非带 auth 的基类,而不是在 behaviors 里临时关掉 authenticator
参数校验别堆在 action 里,用过滤器统一拦截
每个 action 自己写 $request->get() + if (empty()) 容易漏、难维护,且无法复用错误格式。Yii2 的 ActionFilter 是标准解法。
- 建
common/filters/ParamValidatorFilter.php,通过$rules属性按 action ID 配置字段规则 - 严格模式(
$strict = true)建议开启:缺参数直接 400,不留给业务逻辑兜底 - 注意
$request->get($param)和$request->post($param)默认都返回null,不要用isset()判空,要用!== null - 错误信息用
throw new BadRequestHttpException(json_encode(...)),别自己 echo 或 return 数组,否则破坏统一响应结构
微信开发者工具 3.2+ 下 OAuth2 登录失败?重点查 access_token 获取链路
不是 Yii2 不兼容,是新版工具对重定向、跨域、HTTPS 回调更敏感,file_get_contents() 直接请求微信接口极易被拦截或超时。
- 必须用 cURL 替代
file_get_contents(),并显式设置CURLOPT_SSL_VERIFYPEER => false(开发环境)和CURLOPT_TIMEOUT => 15 - 微信返回的
access_token有时带中文错误(如{"errcode":40029,"errmsg":"invalid code"}),要先json_decode($res, true)再判isset($data['access_token']),不能只看 HTTP 状态码 - 前端传来的
code是一次性的,若后端重复使用或延迟超过 5 分钟,必然失败——加日志打点,确认code从收到、到发起请求、到微信响应,全程耗时
behaviors() 里混用 authenticator 和 Cors 的顺序——后者会导致 OPTIONS 预检失败,前端连请求都发不出去。










