Recently, as a result of project development needs, the mobile phone client and the web end of the unified use of a set of interfaces, in order to ensure that sessions (session) can be normal and compatible in various situations, I hope to be able to change the way SessionID access. By default, all Web sites are implemented through cookies in the header header of the HTTP request, and are associated to the server-side corresponding data through the SessionID specified in the Cookie, thereby enabling session functionality.
However, for mobile clients, the original Cookie may not be supported or shielded according to the platform needs, so the development requires that the SessionID be identified by adding a request header x-session-token. In the Laravel framework, the implementation of session initialization, read and start, is implemented through the Illuminate\session\middleware\startsession middleware, the middleware has a key method GetSession, This method is to get SessionId to tell the session component what credentials to restore the session data.
The middleware is registered under the app/http/kernel.php file.
I created a new class to inherit the middleware, while replacing the registration under App/http/kernel.php, the original GetSession method source code is as follows:
Public Function getsession (Request $request)
{
$session = $this->manager->driver ();
$session->setid ($request->cookies->get ($session->getname ()));
return $session;
}
In the new middleware, I modified to:
Public Function getsession (Request $request)
{
$session = $this->manager->driver ();
Determine if the interface is accessible and choose SessionID's access according to the actual situation
if ($request->headers->has (' X-session-token ')) {
$sessionId = $ Request->headers->has (' X-session-token ');
} else {
$sessionId = $request->cookies->get ($session->getname ());
$session->setid ($sessionId);
return $session;
}
But the trouble comes along ...
When you have finished modifying, push to the branch, before merging to the main development branch often need to run a unit test, unfortunately, before the case passed this time unexpectedly error, the problem is CSRF components reported Token error, and we provide in this place Token and usually do not have two, the problem is certainly out in session On
It is noteworthy that I modify the middleware code, the impact on the framework can be said to have no, in fact, did not, because I created the middleware code to change to inherit the middleware code consistent also does not help, but it is strange that I have to change the middleware back to the original middleware does not have this problem.
So I ran the normal and abnormal code, and at the key breakpoint debugging, found that the problem in the middleware of an important attribute $sessionHandled, if the value of false will cause our previous situation. The key is that when the middleware starts, it will take the handle method, and for the session middleware, the first line of the handle method is:
$this->sessionhandled = true;
Interesting ...
We know. The Laravel framework is characterized by its IoC container, in which various classes are initialized to implement various dependency injection to ensure loose coupling between components. Middleware is no exception. You know, the biggest difference between a singleton and a common instance is that no matter how many times it is created, the Singleton is always one, and the attribute in the instance is not initialized, so no problem middleware is necessarily a single example, and the middleware I create myself is just an instance of a normal class. But in the knowledge that it is more to know the reason why, I need to confirm my idea (in fact, the solution has been thought of, said later).
So the problem is basically to initialize the middleware this piece, so have to play the spirit, the Laravel start code carefully. And the focus here is a class called Illuminate\pipeline\pipeline.
This class has three important methods send, through, then. One of the then is the key to start everything. This class is primarily a continuous implementation of several framework startup steps, first of all the components needed to initialize the process (request and middleware), followed by the stack of requests through these processing components (a heap of middleware and routing components), and finally return processing results (Response).
It can be said that this thing is the core of the Laravel Http part (amount, originally is Kernel). Then the problem lies in the Pipeline then method and the Getslice method of its invocation, which directly observes the Getslice method, and discovers that it is responsible for generating the processing stack and instantiating the middleware (middleware) class, the whole method code as follows:
protected function Getslice ()
{return
function ($stack, $pipe) {return
function ($passable) use ($stack, $p IPE) {
if ($pipe instanceof Closure) {return
Call_user_func ($pipe, $passable, $stack);
} else {
list ($ Name, $parameters) = $this->parsepipestring ($pipe);
Return Call_user_func_array ([$this->container->make ($name), $this->method],
array_merge ([$passable, $stack], $parameters));}};}
Note that $this->container->make ($name), which means that it initializes a middleware class, simply make, and if it is not a single example, repeat new, causing the previous attribute to be initialized.
The solution is also obvious, making it a single case.
I added the following line of code to the App/providers/appserviceprovider.php register method to solve the previous problem:
$this->app->singleton (Sessionstart::class); Sessionstart is my middleware class name.
The above introduces the extended Laravel default session middleware caused by the session write failure problem analysis of all the content, I hope you like.