Laravel 9:404页面使用视图合成器时出现Session未设置请求错误
问题分析
全局视图合成器会作用于所有视图(包括404错误视图),但Laravel默认情况下,StartSession中间件属于web中间件组,只有匹配到路由的请求才会执行该中间件。404页面是路由匹配失败后直接渲染的,此时session还未初始化,所以视图合成器里调用session()会抛出Session store not set on request错误。
而把StartSession移到全局中间件组后,虽然错误页面能访问session,但会破坏Laravel的session生命周期逻辑:全局中间件会在所有请求(包括静态资源、API请求)中执行,且缺少web组中其他配套中间件(如ShareErrorsFromSession、VerifyCsrfToken)的协作,导致session数据无法被正确持久化,每次刷新都会重置。
可行解决方案
方案1:在视图合成器中判断session可用性
修改Composer.php的compose方法,先检查当前请求是否已初始化session,再决定是否从session取值:
public function compose(View $view) { // 默认使用配置文件中的货币 $currency = config('app.currency'); // 仅当session已初始化时,从session获取值 if ($this->request->hasSession()) { $currency = $this->request->session()->get('currency', $currency); } $view->with('currency', $currency); }
这种方式最简单,既保留全局视图合成器的作用范围,又避免错误页面报错。
方案2:让错误页面走web中间件组
在routes/web.php中添加一个fallback路由,让404请求先经过web中间件再触发错误:
Route::fallback(function () { abort(404); })->middleware('web');
这样404页面渲染前会执行StartSession中间件,session可正常访问,视图合成器能正确取到session中的货币值。
方案3:缩小视图合成器的作用范围
如果不是所有视图都需要$currency变量,可以在ViewServiceProvider.php中指定合成器作用的视图,排除错误视图:
// 只作用于布局和业务页面视图,排除errors目录下的错误视图 View::composer(['layouts.*', 'pages.*'], \App\Http\View\Composers\Composer::class);
这种方式从根源避免错误视图触发合成器,自然不会出现session相关错误。
内容的提问来源于stack exchange,提问作者Tony Sawlwin

