Recently, due to project development requirements, mobile client and web client use a set of interfaces in a unified manner. To ensure normal Session and compatibility under various circumstances, I hope to change the SessionID acquisition method. By default, all websites are implemented through cookies in the Header of the HTTP request Header. The SessionID specified in the Cookie is used to associate the data on the server to implement the session function.
However, the mobile client may not support the original Cookie or block it based on the platform's needs. Therefore, you must add a request header X-Session-Token to identify the SessionID. In the Laravel framework, Session initialization, reading, and startup are implemented through the Middleware Illuminate \ Session \ Middleware \ StartSession. This Middleware has a key method getSession, this method is to obtain the SessionId to inform the Session component of the creden used to restore Session data.
The middleware is registered in the app/Http/Kernel. php file.
I created a class that inherits the middleware and replaced the registration location 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 changed it:
Public function getSession (Request $ request)
{
$ Session = $ this-> manager-> driver ();
// Determine whether the interface is accessed and select the method for obtaining the SessionID based on 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 troubles also come along...
After the modification, push to the branch. Before merging to the main development branch, you often need to run the unit test. Unfortunately, this error is returned when the previous Case is passed, the problem is that the CSRF component reports a Token error, and the Token we provide here is not the same as that in normal times. The problem must be in the Session.
It is worth noting that I have modified the middleware code and have no impact on the framework. In fact, it does not, it doesn't matter if I change the self-created middleware code to the inherited middleware code, but it's strange that I don't have this problem when I switch the middleware back to the original middleware.
So I ran all the code under normal and abnormal circumstances, and breakpoint debugging at key points. I found that the problem lies in an important attribute of middleware $ sessionHandled, if this value is false, it will cause our previous situation. The key is that the handle method is used when the middleware is started. For the Session middleware, the first line of code of the handle method is:
$ This-> sessionHandled = true;
Interesting...
We know. The Laravel framework features its IoC container. Each type of initialization in the framework is the responsibility of it to implement various dependency injection to ensure loose coupling between components. The middleware is certainly no exception. You must know that the biggest difference between a Singleton and a common instance is that, no matter how many times a singleton is created, the singleton will always be one and the attributes in the instance will not be initialized, therefore, the non-problematic middleware must be a singleton, and the middleware I created myself is only a common class instance. But I need to confirm my idea in the spirit of knowing it, and I have come up with a solution, which will be explained later ).
The problem is that the middleware is initialized, so you have to take a look at Laravel's startup code carefully. The focus here is a class called Illuminate \ Pipeline.
This class has three important methods: send, through, and then. Then is the key to start everything. This class is mainly used to continuously execute several startup steps of the framework. The first is to initialize the components (Request and middleware) required in the processing process ), the second is the stack composed of the request processing components (a bunch of middleware and routing distribution components), and the last is the Response ).
It can be said that this is the core of Laravel Http (the amount is actually the Kernel ). The problem is that the Pipeline's then method and the getSlice method it calls directly observe the getSlice method. It can be found that it is responsible for generating the processing stack and instantiating the Middleware (Middleware) class, the code for the entire method is as follows:
Protected function getSlice ()
{
Return function ($ stack, $ pipe ){
Return function ($ passable) use ($ stack, $ pipe ){
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 ));
}
};
};
}
You can note that $ this-> container-> make ($ name) means that it initializes a middleware class, which is simply make. If it is not a singleton, it repeats new, as a result, the previous attribute is initialized.
Then the solution is obvious, making it a singleton.
I added the following code to the register method of app/Providers/AppServiceProvider. php to solve the previous problem:
$ This-> app-> singleton (SessionStart: class); // SessionStart is the name of my middleware class.
It's actually that simple. This article ?? Are you sure you want to know? Lusi? Lucky enough ??~
To sum up, this is actually a typical problem. For instances of classes that are used repeatedly within a complete program life cycle, if they need to maintain the context state, you must make it a singleton instead of the result after repeated instantiation. This Session problem is nothing more than the built-in middleware that is registered as a singleton by default, but what I customize is not. The solution is just like the problem. Many of the problems in daily development are caused by various ignoring the nature of the framework.