最近、プロジェクト開発のニーズにより、モバイル クライアントと Web ページは、さまざまな状況下でセッションが正常で互換性があることを保証するために、統一されたインターフェイスを使用するようになりました。セッションIDの方法。デフォルトでは、すべての Web サイトは HTTP リクエストのヘッダー ヘッダーの Cookie によって実装され、Cookie に指定された SessionID がサーバー側の対応するデータに関連付けられてセッション機能が実装されます。
ただし、モバイル クライアントの場合、元の Cookie がサポートされないか、プラットフォームのニーズに応じてブロックされる可能性があるため、開発では SessionID を識別するためのリクエスト ヘッダー X-Session-Token を追加する必要があります。 Laravel フレームワークでは、セッションの初期化、読み取り、開始はすべて IlluminateSessionMiddlewareStartSession ミドルウェアによって実装されます。このミドルウェアには、SessionId を取得し、セッション データを復元するために使用する資格情報をセッション コンポーネントに伝えるための重要なメソッドがあります。
このミドルウェアは app/Http/Kernel.php ファイルの下に登録されています。
ミドルウェアを継承する新しいクラスを作成し、登録場所をapp/Http/Kernel.php以下に置き換えました。 元のgetSessionメソッドのソースコードは次のとおりです。
public function getSession(Request $request){ $session = $this->manager->driver(); $session->setId($request->cookies->get($session->getName())); return $session;}
In In新しいミドルウェアを
public function getSession(Request $request){ $session = $this->manager->driver(); // 判断是否是接口访问并根据实际情况选择 SessionID 的获取方式 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;}
に変更しましたが、問題も発生しました。 。 。
変更後、メインの開発ブランチにマージする前に、単体テストを実行する必要があることがよくあります。残念ながら、今回はエラーが報告されました。 CSRF コンポーネントがトークン エラーを報告しました。ここで提供するトークンは通常のものと変わりません。問題はセッションにあるはずです。
ミドルウェア コードを変更しても、フレームワークにはまったく影響を与えないと言えることに注意してください。実際には、作成したミドルウェア コードを変更したためです。継承されたミドルウェア コードの一貫性も役に立ちませんが、不思議なことに、ミドルウェアを元のミドルウェアに戻した後は、この問題は発生しません。
そこで、通常の条件と異常な条件でコードを実行し、重要なポイントにブレークポイントを使用してデバッグしたところ、問題はミドルウェアの重要な属性である $sessionHandled にあることがわかりました。値が false の場合は、以前の状況を引き起こしたのです。重要なのは、ミドルウェアの起動時にハンドル メソッドが使用されることです。セッション ミドルウェアの場合、ハンドル メソッドのコードの最初の行は次のとおりです。 。 。
$this->sessionHandled = true;
それでは、問題は大体ミドルウェアの初期化なので、気を取り直してLaravelのスタートアップコードを詳しく見てみる必要があります。ここでの重要なポイントは、IlluminatePipelinePipeline というクラスにあります。
このクラスには、send、through、then という 3 つの重要なメソッドがあります。それでは、すべてを開始する鍵はどこにあるのでしょうか。このクラスは主にフレームワークの複数の起動ステップを連続的に実行するもので、1つ目は処理プロセスに必要なコンポーネント(リクエストとミドルウェア)を初期化し、2つ目はこれらの処理コンポーネント(集合体)で構成されるスタックにリクエストを渡すことです。ミドルウェアやルーティングディスパッチコンポーネントなど)を実行し、最後に処理結果(Response)を返します。
これは、Laravel Http 部分 (まあ、本来は Kernel です) の中核とも言えるものです。前述の問題は、Pipeline の then メソッドとそれが呼び出す getSlice メソッドにあります。 getSlice メソッドを直接観察すると、このメソッドが処理スタックの生成と Middleware クラスのインスタンス化を担当していることがわかります。メソッド コード全体は次のとおりです。 >
$this->container->make($name) に注目してください。これは、ミドルウェア クラスを初期化することを意味します。これは、単に make です。シングルトンでない場合は、new が繰り返されます。 、その結果、以前のプロパティが初期化されます。
解決策は明らかで、シングルトンにします。
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)); } }; };}
次のコード行を app/Providers/AppServiceProvider.php の register メソッドに追加することで、前の問題を解決しました。
実際、それはとても簡単です。この記事はとても長いので、皆さんがもっと面白いことを学べることを願っています~~
$this->app->singleton(SessionStart::class); // SessionStart 是我那个中间件类名