laravel 5.5+ 中间件须继承 illuminate\http\middleware\trustproxies 或直接实现 handle 方法,9+ 强制 handle($request, closure $next),且顺序必须符合 encryptcookies→startsession→authenticate 等依赖链,否则 session、auth 失效或静默终止。

中间件类名和命名空间在 Laravel 5.5+ 有硬性变更
如果你从 Laravel 5.4 或更早升级,app/Http/Middleware 下的中间件类如果还继承 Illuminate\Foundation\Http\Middleware\TrimStrings 这类“框架内置中间件基类”,会直接报错——因为这些类在 5.5+ 已被移除或重构为内部使用。
真正该继承的是 Illuminate\Http\Middleware\TrustProxies 这类明确标为 Middleware 的抽象类,或者干脆不继承任何基类,只实现 handle 方法。
- 旧写法(5.4 及以前):
class CheckAge extends BaseMiddleware→ 现在找不到BaseMiddleware - 新写法(5.5+ 推荐):
class CheckAge implements Middleware,或直接class CheckAge+public function handle($request, Closure $next) - 自定义中间件注册方式没变,仍是
$middleware/$routeMiddleware数组,但键名不能含点号(如auth.basic是框架自带的,你自己别起my.auth)
Laravel 9+ 的中间件参数签名变了:$request 第二个参数必须是 Closure
5.5 到 8.x 的 handle 方法签名是 handle($request, Closure $next, $param = null),允许可选参数;Laravel 9 开始强制要求第二个参数必须是 Closure $next,且不允许额外位置参数——否则启动时抛出 TypeError,提示 “Argument 2 passed to … must be an instance of Closure”。
- 错误写法:
public function handle($request, $next, $role)→ 9+ 直接挂 - 正确写法:
public function handle($request, Closure $next),角色等参数改用构造函数注入或配置传入 - 若需动态参数(比如
can:edit-post),必须走Route::middleware('can:edit-post')形式,由框架解析后传进handle的第三个参数(即...$parameters)
中间件顺序错乱导致 auth 和 session 不生效
常见现象:登录后 Auth::check() 始终返回 false,或 session() 数据读不到。根本原因不是中间件没写对,而是它在 $middleware 数组里放错了位置。
Laravel 要求 StartSession 必须在 EncryptCookies 之后、所有依赖 session 的中间件(如 Authenticate、ShareErrorsFromSession)之前;Authenticate 又必须在 SubstituteBindings 之后才能正确解析路由模型绑定里的用户。
- 危险顺序:
Authenticate→StartSession→EncryptCookies→ 页面白屏或 session 丢失 - 安全顺序(参考官方默认):
EncryptCookies→AddQueuedCookiesToResponse→StartSession→ShareErrorsFromSession→Authenticate - 自定义中间件若依赖 session,请确保它排在
StartSession之后;若要操作加密 cookie,请排在EncryptCookies之前
中间件响应提前终止,后续中间件和控制器完全不执行
这是最隐蔽也最容易误判的问题:你写了 return response()->json(['error' => 'no access']),结果发现日志没打、事件没触发、甚至数据库事务没回滚——因为 return 后整个请求生命周期就断了。
中间件不是“过滤器”,而是“拦截器”。一旦你显式 return,框架立刻跳过后续所有中间件和控制器逻辑,直接发响应。
- 想记录日志再放行?不能
return Log::info(...); $next($request),得写成Log::info(...); return $next($request) - 想做权限校验并统一返回 403?可以,但确认这是你真正想要的行为——它确实会跳过控制器,包括你可能写的
__destruct或 try-finally 清理逻辑 - 调试时可在中间件开头加
dd(get_class($this), debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)),快速确认是否被提前终止











