Laravel中间件执行顺序由数组位置决定,而非注册顺序:请求进入时从前到后执行,响应返回时逆序执行;跨中间件通信应通过$request->attributes安全传递数据。

中间件执行顺序由数组顺序决定,不是注册顺序
你在 app/Http/Kernel.php 里往 $middlewareGroups 或 $middleware 里加中间件,它们的执行顺序只取决于数组里的位置——靠前的先执行(进入时),靠后的后执行(进入时);响应返回时则反过来。很多人误以为「先注册就先执行」或「按类名字母序」,其实完全不是。
常见错误现象:auth 放在 throttle 后面,结果未登录用户也被限流了;或者 cors 放太后面,导致预检请求没被处理就进到路由里报 404。
- 进入请求:数组索引从小到大依次调用
handle() - 响应返回:从最后一个中间件开始,逆序调用
next($request)的返回值处理逻辑 - 全局中间件(
$middleware)会作用于所有请求,慎加耗时操作 - 路由组中间件(如
web、api)只影响对应路由,优先选它而不是全局堆砌
多个中间件共享状态只能靠 Request 对象传参
Laravel 中间件之间不自动共享变量或上下文。你想让 CheckRole 知道 AuthMiddleware 解出来的用户 ID?不能靠闭包变量或静态属性——那会引发并发污染或测试难问题。
正确做法是把数据塞进 $request 实例,用 $request->attributes->set('key', $value),下游中间件再用 $request->attributes->get('key') 拿。这是 Laravel 官方推荐的跨中间件通信方式,安全且可测。
- 别用
session()或Cache::store('array')临时存中间状态——生命周期不可控 - 避免在中间件里修改
$request->user()返回值,除非你重写了整个认证逻辑 - 如果只是传递简单标识(如是否已鉴权),用
$request->has('auth_checked')这类标记更轻量
带参数的中间件写法和路由绑定要对得上
像 throttle:60,1 这种带参数的中间件,本质是调用 ThrottleRequests::__construct('60,1'),参数字符串会被自动拆解。但如果你自定义中间件,比如 CheckPermission:post.create,就必须在中间件构造函数里接收 ...$parameters,再手动解析。
常见错误:路由里写 middleware(['check.permission:post.create']),但中间件类没声明可变参数,结果报 Too few arguments 错误。
- 自定义中间件构造函数必须写成
public function __construct(...$parameters) - 参数在路由中用英文逗号分隔,如
check.role:admin,editor,$parameters就是['admin', 'editor'] - 别在中间件里做 heavy 初始化(如查数据库),构造阶段只存参数,真正逻辑放
handle()里
调试中间件执行链最有效的办法是 dump die + 日志打点
光看路由列表和中间件数组,很难确认某条请求到底走了哪些中间件。别依赖 IDE 跳转或文档猜测,直接在关键中间件的 handle() 开头加 Log::debug('In CheckAuth');,或者更暴力点:dd(get_class($this), $request->url());。
注意:不要在生产环境用 dd(),但开发时它是最快定位执行路径断裂点的方式。比如你发现 VerifyCsrfToken 没触发,很可能是表单没带 _token 字段,导致在进入中间件前就被框架拦截了。
-
php artisan route:list --middleware只显示注册关系,不反映实际执行路径 - 如果某个中间件完全没日志输出,先检查它是否被正确分配到该路由组(比如 API 路由默认不用
web组中间件) - 异步队列或 API 请求可能绕过部分中间件(如
StartSession),这点容易被忽略










