CakePHP 4 的中间件必须在 src/Application.php 的 Application::middleware() 方法中注册,该方法返回 MiddlewareQueue 实例,按添加顺序执行;漏注册则中间件不生效。

中间件注册位置在哪(src/Application.php)
CakePHP 4 的中间件必须在 Application::middleware() 方法里注册,不是在路由文件、控制器或配置里。这个方法返回一个 MiddlewareQueue 实例,所有中间件按添加顺序执行。漏掉这一步,中间件根本不会被加载。
- 中间件类必须实现
Psr\Http\Server\MiddlewareInterface,否则会报 TypeError: Argument 1 passed to Cake\Http\BaseApplication::add() must be an instance of Psr\Http\Server\MiddlewareInterface
- 推荐用类名字符串方式注册(如
MyAuthMiddleware::class),而不是匿名函数——后者无法被容器自动解析依赖
- 如果中间件需要访问服务(比如
AuthenticationService),得在构造函数里声明并让 DI 容器注入,不能手动 new
写一个最简可用的中间件类
中间件本质就是一个处理 $request 和 $response 的函数式逻辑,核心是 process() 方法。别试图在里面 redirect 或 render,那属于控制器职责。
namespace App\Middleware;
<p>use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;</p><p>class LogIpMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$ip = $request->getServerParams()['REMOTE_ADDR'] ?? 'unknown';
error_log("[MW] Request from {$ip}");</p><pre class="brush:php;toolbar:false;"> return $handler->handle($request);
}
Psr\Http\Server\MiddlewareInterface,否则会报 TypeError: Argument 1 passed to Cake\Http\BaseApplication::add() must be an instance of Psr\Http\Server\MiddlewareInterface
MyAuthMiddleware::class),而不是匿名函数——后者无法被容器自动解析依赖AuthenticationService),得在构造函数里声明并让 DI 容器注入,不能手动 new$request 和 $response 的函数式逻辑,核心是 process() 方法。别试图在里面 redirect 或 render,那属于控制器职责。
namespace App\Middleware;
<p>use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;</p><p>class LogIpMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
$ip = $request->getServerParams()['REMOTE_ADDR'] ?? 'unknown';
error_log("[MW] Request from {$ip}");</p><pre class="brush:php;toolbar:false;"> return $handler->handle($request);
}}
- 必须调用
$handler->handle($request)向下传递请求,否则后续中间件和控制器全被拦住 - 不要修改
$request后不传给$handler,那是常见断链原因 - 日志、权限检查、CORS 头注入这类无副作用逻辑适合放这里;涉及数据库或 session 写入的,注意事务边界
中间件执行顺序怎么控制(before/after Router)
中间件顺序直接影响行为逻辑。比如鉴权中间件必须在 RoutingMiddleware 之后才能拿到匹配的路由参数,但又得在 AuthorizationMiddleware 之前做基础 IP 过滤。
- 在
Application::middleware() 中,越早 add() 的中间件越先执行(请求阶段),响应阶段则倒序执行
- 常见陷阱:把日志中间件加在最前,结果它记录不到路由信息;应该加在
RoutingMiddleware 之后、AuthorizationMiddleware 之前
- 使用
$middleware->add(new MyMiddleware()) 是追加;用 $middleware->insertBefore('RoutingMiddleware', ...) 或 insertAfter() 更精准定位
为什么中间件里拿不到 Session 数据
CakePHP 4 默认的 SessionMiddleware 是按需启用的,且只在 RoutingMiddleware 之后才初始化 session。如果你的中间件注册在它前面,$request->getSession() 就是 null。
- 查看
config/bootstrap.php 是否启用了 SessionMiddleware;默认模板里它是被注释掉的
- 确保你的中间件在
SessionMiddleware 之后注册,比如用 insertAfter('SessionMiddleware', ...)
- 不要依赖
$request->getAttribute('session') 在中间件早期阶段存在——它由 SessionMiddleware 注入,不是 request 自带属性
Application::middleware() 中,越早 add() 的中间件越先执行(请求阶段),响应阶段则倒序执行RoutingMiddleware 之后、AuthorizationMiddleware 之前$middleware->add(new MyMiddleware()) 是追加;用 $middleware->insertBefore('RoutingMiddleware', ...) 或 insertAfter() 更精准定位SessionMiddleware 是按需启用的,且只在 RoutingMiddleware 之后才初始化 session。如果你的中间件注册在它前面,$request->getSession() 就是 null。
- 查看
config/bootstrap.php是否启用了SessionMiddleware;默认模板里它是被注释掉的 - 确保你的中间件在
SessionMiddleware之后注册,比如用insertAfter('SessionMiddleware', ...) - 不要依赖
$request->getAttribute('session')在中间件早期阶段存在——它由 SessionMiddleware 注入,不是 request 自带属性
中间件不是万能钩子,它没有控制器上下文,也不参与 ORM 生命周期。真正复杂的业务逻辑,该放 service 层还是得放 service 层。











