yii 2.0 无原生中间件,behaviors() + event 是唯一可复用的请求前/后处理方案;需在 events() 中监听 event_before_action,通过 $event->isvalid = false 拦截,并确保控制器中调用 parent::beforeaction($event) 触发事件。

Yii 2.0 没有原生中间件,behaviors() 是最接近的替代方案
Yii 2.0 的核心设计里压根没有 middleware 这个概念,官方不支持像 Laravel 或 Express 那样的请求管道。想在控制器执行前后统一加逻辑(比如权限校验、日志记录、参数预处理),唯一靠谱的路径就是靠 behaviors() + Event。它不是中间件,但能模拟出“类似中间件”的行为链条。
常见错误是试图在 beforeAction() 里塞一堆逻辑——这会导致复用性差、测试困难、且无法被其他控制器继承或动态开关。
-
behaviors()返回数组,每个行为必须是yii\base\Behavior子类或配置数组 - 行为类中监听
Controller::EVENT_BEFORE_ACTION和Controller::EVENT_AFTER_ACTION事件 - 别在行为里直接
return false中断流程——要用$event->isValid = false,否则 Yii 不会识别为“拦截” - 行为可以按 action 名过滤:用
'only' => ['create', 'update']控制生效范围
写一个可复用的权限检查行为,避免重复写 Yii::$app->user->can()
手动在每个 actionXxx() 开头写权限判断,既啰嗦又容易漏掉 throw new ForbiddenHttpException()。封装成行为后,一行配置就能挂载,还能集中管理提示文案和跳转逻辑。
典型坑是权限检查时机不对:如果在 init() 里检查,此时 action 尚未确定,拿不到路由参数;必须在 EVENT_BEFORE_ACTION 中做,且确保 $event->action 可用。
- 行为类里重写
events()方法,返回[Controller::EVENT_BEFORE_ACTION => 'checkPermission'] -
checkPermission($event)中通过$event->action->id和$event->action->controller->id构建权限项名(如'post/update') - 检查失败时调用
$event->isValid = false,再手动throw new ForbiddenHttpException('无权操作') - 不要依赖
Yii::$app->user->isGuest做前置判断——权限系统应统一由can()处理,guest 用户自然返回 false
beforeAction() 和行为事件谁先触发?顺序错了就拦不住请求
行为里的事件监听器是在 beforeAction() **内部**触发的,也就是说:beforeAction() 先开始执行 → 触发所有注册的 EVENT_BEFORE_ACTION → 然后才真正调用你的 actionXxx()。这个顺序决定了你能不能“真正拦截”。
如果在控制器自己的 beforeAction() 里写了 return parent::beforeAction($event),而父类又没做任何事,那行为事件根本不会触发——因为 Yii 默认的 Controller::beforeAction() 是空实现,不主动触发事件。必须显式调用 parent::beforeAction($event) 才行。
- 控制器中重写
beforeAction()时,第一句必须是if (!parent::beforeAction($event)) { return false; } - 否则行为绑定的
EVENT_BEFORE_ACTION根本不会派发 - 验证方法:在行为的
checkPermission()里打日志,发现没输出,八成是忘了调parent::beforeAction() - 行为无法干预
beforeAction()自身的返回值,只能影响后续动作是否执行
行为里访问 $this->owner 要小心作用域和生命周期
$this->owner 在行为里指向挂载它的控制器实例,但它不是随时都可用。比如在行为构造函数里访问,$this->owner 还是 null;在 init() 里访问,通常已赋值,但控制器的属性可能还没初始化完毕(比如 $this->owner->module 可能为空)。
最容易踩的坑是:在行为里缓存 $this->owner->request 或 $this->owner->response 到属性中,结果下一次请求复用该行为实例时,拿到的是上一个请求的 request/response 对象——Yii 2.0 的行为默认是单例复用的,除非显式配置 'class' => MyBehavior::className(), 'enabled' => true 并确保无状态。
- 所有对
$this->owner的访问,只应在事件回调(如checkPermission())中进行 - 避免在行为中保存控制器相关对象的引用,尤其是
Request、Response、Session - 如果真要共享数据,用
Yii::$app->params或临时存到$event->data(仅限当前事件周期) - 行为类本身不应有状态,否则多请求并发时可能串数据
owner 访问过早、或者以为 return false 就能中断流程。











