Yii2中behaviors()定义的过滤器按数组声明顺序执行,先beforeAction正序、后afterAction逆序;AccessControl必须置于首位以拦截非法请求,全局过滤器优先于控制器级行为。

behaviors() 里定义的过滤器按数组顺序执行
Yii2 的过滤器本质是 ActionFilter 行为,它们在控制器的 behaviors() 返回的数组中声明。这个数组不是无序集合——**顺序就是执行顺序**。
比如你写了:
public function behaviors()
{
return [
'access' => ['class' => AccessControl::class, /* ... */],
'verbs' => ['class' => VerbFilter::class, /* ... */],
'cache' => ['class' => HttpCache::class, /* ... */],
];
}那么请求进来时,会依次触发:AccessControl::beforeAction() → VerbFilter::beforeAction() → HttpCache::beforeAction()。
- 只要任意一个
beforeAction()返回false,后续所有过滤器和目标 action 都不会执行 -
afterAction()则反向执行:cache → verbs → access(后注册的先收尾) - 别指望靠 PHP 数组键名排序或配置项位置“隐式调整顺序”,
['a'=>..., 'b'=>...]和[..., ...]在 PHP 中迭代顺序一致,但语义上推荐用数字索引明确意图
AccessControl 必须放在最前面才能拦住非法请求
如果你把 AccessControl 放在 VerbFilter 后面,就可能出现「用户没登录,但 POST 请求先被 VerbFilter 拦下报 405」这种误导性错误——实际该拦的是权限,不是方法。
正确做法是把权限控制行为放在 behaviors 数组首位:
return [
'access' => ['class' => AccessControl::class, 'rules' => [...]],
'verbs' => ['class' => VerbFilter::class, 'actions' => [...]],
// 其他过滤器...
];-
AccessControl依赖Yii::$app->user状态,而 user 组件初始化早于所有行为 attach,所以它能安全调用can() - 如果用了自定义
ActionFilter做日志或参数预处理,且逻辑依赖用户身份,也得排在access后面,否则$action可能根本没机会进到你的过滤器 - 别写成
'access' => ['class' => 'yii\filters\AccessControl', ...]这种字符串类名——虽然能跑,但 IDE 不识别、无法跳转,容易拼错
全局过滤器和控制器级过滤器谁先执行?
全局过滤器(在 config/web.php 的 as 配置)比控制器自身的 behaviors() 更早介入。
执行链实际是:
Application::run() → 解析路由 → 创建控制器 → 触发 Controller::beforeAction() → 此时先运行全局行为(as),再运行控制器 behaviors() 数组里的行为。
- 全局行为必须继承
ActionFilter,且不能用only/except控制范围(因为还没确定 controller/action);如需限定范围,得在beforeAction()里手动判断$action->getRoute() - 常见误配:
'as access' => ['class' => 'yii\filters\AccessControl']放全局,但没配rules,结果所有请求 403 —— 因为默认规则是空数组,即全部拒绝 - 控制器级
behaviors()会覆盖同名全局行为(比如都叫access),不是叠加
beforeAction 返回 false 后,afterAction 还会执行吗?
不会。一旦某个 beforeAction() 返回 false,Yii 就中断当前 action 执行流程,直接跳转到响应阶段,**所有后续的 beforeAction() 和全部 afterAction() 都跳过**。
- 这意味着你在
AccessControl里 deny 掉请求,HttpCache的afterAction()就不会设置缓存头 - 如果你想确保某些清理逻辑(比如日志记录、资源释放)一定执行,不要放
afterAction(),改用try/finally或事件监听Controller::EVENT_AFTER_ACTION - 注意:
beforeAction()里抛异常(如throw new ForbiddenHttpException())等价于返回false,同样跳过后续过滤器
真正容易被忽略的是:过滤器顺序不是靠注释或命名约定保证的,而是靠数组字面量的书写顺序硬编码决定的。改个配置项位置,可能就让权限检查失效了。


















