启用StartSession后自定义Session-based中间件失效问题求助
先把你的中间件代码贴出来方便咱们一起梳理:
public function handle(Request $request, Closure $next) { if ($request->isMethod('get') && (!$request->hasSession() || $request->session()->pull('framed', false) == false)) { return response()->view('dashboard.frame'); } $response = $next($request); if ($response instanceof RedirectResponse && $request->hasSession()) { $request->getSession()->flash('framed', true); } return $response; }
核心原因:中间件执行顺序搞反了
Laravel的中间件是严格按注册顺序依次执行的,全局中间件的优先级最高,会在所有路由中间件之前运行。
你之前的中间件能正常工作,是因为StartSession(负责初始化Session的核心中间件)是在web这类路由中间件组里注册的,而且你的自定义中间件是在StartSession之后执行的——这时候Session已经完成初始化,$request->hasSession()会返回true,你能正确读取和操作Session里的framed值,逻辑自然没问题。
但当你把StartSession加到全局中间件后,如果你的自定义中间件注册在它前面,就会直接触发bug:
- 你的中间件先跑,此时Session还没被初始化,
$request->hasSession()返回false,直接命中第一个条件,每次GET请求都返回dashboard.frame视图; - 哪怕后续
StartSession完成了Session初始化,你的中间件逻辑已经走完,完全没用到Session里的framed标记,导致原本的“判断是否已加载过frame”的逻辑直接失效。
修复方案:调整中间件顺序
只要确保你的自定义中间件在StartSession之后执行就能解决问题,分两种场景处理:
如果你的自定义中间件是全局中间件
在app/Http/Kernel.php的$middleware数组里,把你的中间件移到Illuminate\Session\Middleware\StartSession后面:protected $middleware = [ // ...其他全局中间件 \Illuminate\Session\Middleware\StartSession::class, \App\Http\Middleware\YourCustomMiddleware::class, // 放到StartSession之后 ];如果你的自定义中间件是路由中间件
确保你的路由使用的中间件组(比如默认的web组)里,StartSession在你的自定义中间件之前。默认web组已经包含StartSession,只要你的自定义中间件在组内顺序靠后即可,或者直接在路由上按顺序挂载:Route::get('/dashboard', [DashboardController::class, 'index']) ->middleware([StartSession::class, YourCustomMiddleware::class]);
额外小提醒
注意$request->session()->pull('framed', false)这个方法会取出并删除Session里的framed值,如果后续业务还有需要用到这个标记的场景,可能要改成get方法(只读取不删除),不过从你的代码逻辑来看,用pull是合理的——毕竟你只需要在重定向后的第一次GET请求里判断一次,之后这个标记就没用了。
内容的提问来源于stack exchange,提问作者Andrew Mast

