必须基于 psr-15 中间件实现请求拦截,因 yii 3.0 废除控制器钩子、由中间件栈驱动生命周期;中间件按 config/web.php 中 middleware 数组顺序执行,需实现 middlewareinterface 并注入依赖,支持全局或路由组(routegroup)注册。

要在 Yii 3.0 中实现请求拦截,必须绕过 Yii 2.x 的行为(Behavior)和控制器钩子,直接基于 PSR-15 中间件构建拦截逻辑——因为 Yii 3.0 的请求生命周期完全由中间件栈驱动,【控制器不再有 beforeAction、afterAction 等钩子,任何拦截都必须在中间件中完成】。
理解 Yii 3.0 请求入口与中间件挂载点
Yii 3.0 的入口文件 index.php 不再调用 Yii::$app->run(),而是构造一个 yii\web\Application 实例并调用其 handle() 方法,该方法接收 PSR-7 Request 并返回 PSR-7 Response。整个流程由中间件管道串联,【中间件执行顺序严格按 config/web.php 中 middleware 数组的定义顺序,从左到右依次执行】。
打开 config/web.php,找到 'middleware' 键,它是一个数组,每个元素是一个中间件类名或可调用数组,例如 ['class' => \App\Middleware\AuthMiddleware::class]。
中间件类必须实现 Psr\Http\Server\MiddlewareInterface,并在 process() 方法中决定是否继续调用 $handler->handle($request)。
编写一个基础权限拦截中间件
方法一:手动实现 MiddlewareInterface
创建 src/Middleware/PermissionMiddleware.php:
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Yiisoft\Http\Status;
class PermissionMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$path = $request->getUri()->getPath();
if (str_starts_with($path, '/admin') && !$this->hasAdminRole()) {
return new \Yiisoft\Http\Response\HtmlResponse('Access denied', Status::FORBIDDEN);
}
return $handler->handle($request);
}
private function hasAdminRole(): bool
{
// 此处应从容器获取用户服务,不能用 Yii::$app->user
return false;
}
}
这一步不能直接访问 Yii::$app->user,因为 ServiceLocator 已被移除;必须通过构造函数注入依赖,比如 UserIdentityInterface 或 SessionInterface。
注册中间件并控制作用范围
第一步:在 config/web.php 的 middleware 数组中添加中间件实例
'middleware' => [
['class' => \App\Middleware\PermissionMiddleware::class],
// 其他中间件…
],
第二步:若只想对特定路由组生效,改用路由级中间件声明
在 config/routes.php 中,使用 RouteGroup:
use Yiisoft\Router\Group;
use App\Middleware\PermissionMiddleware;
return [
Group::create('/admin')
->addMiddleware(PermissionMiddleware::class)
->addRoutes([/* admin 路由 */]),
];
注意:RouteGroup 的中间件只对该组内所有路由生效,不会影响 /api 或 /public 路径。
第三步:确保中间件类已正确注册到 DI 容器(如果它依赖其他服务)
在 config/common.php 中添加:
App\Middleware\PermissionMiddleware::class => [‘class’ => App\Middleware\PermissionMiddleware::class, ‘__construct()' => [UserIdentityInterface::class]],
调试中间件执行顺序与中断点
方法一:在中间件 process() 开头插入日志输出
file_put_contents('/tmp/middleware.log', "PermissionMiddleware triggered for {$request->getUri()->getPath()}\n", FILE_APPEND);
方法二:用 response header 标记中间件执行痕迹
return $handler->handle($request)->withAddedHeader('X-Middleware', 'PermissionChecked');
方法三:在中间件中主动抛出异常终止流程
if (!$this->isAllowed($request)) {
throw new \Yiisoft\Web\Exceptions\ForbiddenHttpException('Insufficient permissions');
}
这会触发全局异常处理器,返回 403 响应,但要注意:异常会跳过后续中间件,且不经过 $handler->handle()。











