Laravel Session 遇到的坑
这两天遇到了一个很奇怪的问题,更新session ,session的值不变。经过一番追查,终于找到问题,并搞明白了原理。写这篇博客记录下。
框架版本
Laravel 5.4
问题
先来描述下问题,我在我们项目基础的Middleware中,加入session操作,存入了一个值,再在Controller中取出使用,大致代码如下:
// Middleware
public function handle($request, Closure $next)
{
$id = Redis::get('id');
session(['id' => $id]);
return $next($request);
}
// Controller
public function index()
{
$id = session('id');
return ['id' => $id];
}
假设reids中的id是1,这一次访问index这个action,返回的是1,当你将redis中id的值改成2时,在访问,发现返回的还是1,而且之后的访问也都是1。这里说明一下session使用的是redis 。
解决问题
看到这样神奇的结果,百思不得其解。于是打开Xdebug,开始调试。经过多次调试,发现在执行完\Illuminate\Session\Middleware\StartSession这个Middleware后,session里面的值就变回1了,在之前都是2。然后想到会不会我们的Middleware在StartSession之前执行造成的,将我们的Middleware移到StartSession之后,发现果然可以了,app/Http/Kernel.php中的代码如下:
protected $middlewareGroups = [
'web' => [
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\OurMiddleware::class,
]
];
其中的OurMiddleware是我们自己写的Middleware,之前是放在最上面的,$next($request)之前的代码的执行顺序是从上到下的,如果OurMiddleware中有些内容是必须在最开始的,可以考虑分成两个Middleware。
理解原理
虽然解决了问题,但还是不知道其原理究竟是怎样的,带着这样的疑问我继续查看源码,最终找到了相应的内容。
-
session不是实时落地的,也就是说当你调用session(['id' => $id])时,id并没有被真正存入redis中,而是缓存在\Illuminate\Session\Store单例的attributes属性中,可以查看其put方法,代码如下:public function put($key, $value = null) { if (! is_array($key)) { $key = [$key => $value]; } foreach ($key as $arrayKey => $arrayValue) { Arr::set($this->attributes, $arrayKey, $arrayValue); } } -
\Illuminate\Session\Middleware\StartSession在执行时,回自动加载redis中已经实例化的数据,并覆盖\Illuminate\Session\Store单例中的attributes属性,所以这就导致我们一直取到的都是redis中的session数据。加载覆盖的代码如下:protected function loadSession() { $this->attributes = array_merge($this->attributes, $this->readFromHandler()); } protected function readFromHandler() { if ($data = $this->handler->read($this->getId())) { $data = @unserialize($this->prepareForUnserialize($data)); if ($data !== false && ! is_null($data) && is_array($data)) { return $data; } } return []; }
其中的readFromHandler方法就是获取redis中的session数据。
后记
其实这不是Laravel session的坑,是我自己踩坑,原谅我是个标题党:smile:
本作品采用《CC 协议》,转载必须注明作者和本文链接
关于 LearnKu
推荐文章: