Laravel中间件采用洋葱模型:请求从全局→路由组→路由级逐层进入,响应则逆向返回;需return $next($request)维持链路,terminate()异步执行,顺序由注册位置决定。

请求和响应在 Laravel 中间件里走的是“洋葱模型”:请求一层层往里穿,响应一层层往外返。
请求阶段:从外到内逐层进入
当一个 HTTP 请求进来,它会按顺序经过:
- 全局中间件(
$middleware)——最外层,所有请求必经 - 路由组中间件(如
web或api组)——按路由分组激活 - 路由级中间件(
->middleware('xxx'))——最靠近控制器,最后执行
每一层都调用 $next($request) 把请求交给下一层。比如你写了 auth → throttle → can:edit,那请求先过 auth,再 throttle,最后 can,然后才到控制器。
响应阶段:从内到外逆向返回
控制器返回响应后,流程原路折返:
- 路由级中间件最先收到响应(在
$next($request)调用之后) - 接着是路由组中间件
- 最后是全局中间件(最外层,最后处理响应)
这意味着你可以在中间件的 handle() 方法里,在 $next($request) 前做请求预处理(如检查 token),在它之后做响应后置操作(如加 header、记录耗时)。
关键细节不能漏
-
必须写
return $next($request),否则请求链中断,控制器收不到请求,也不报错 - 中间件类里的
terminate()方法是异步执行的,不参与主流程,只适合清理资源(如关闭连接、写日志) - 顺序不是靠数字优先级决定,而是由注册位置决定:数组里越靠前,越“靠外”,请求时最早执行、响应时最晚执行
- 隐式依赖要小心,比如
StartSession必须在VerifyCsrfToken之前,否则 session 数据读不到,CSRF 验证就失败
怎么验证实际执行顺序
别靠猜,动手看:
- 在每个中间件
handle()开头加Log::info('In MyMiddleware', ['path' => $request->path()]) - 或临时用
dd('MyMiddleware')中断流程,观察执行路径(上线前务必删掉)











