Laravelのミドルウェアおよびパイプライン機能





Laravelは、Web開発者の日常的なタスクのほとんどを最もエレガントな方法で解決し、可能な限り多くのツールを収集しようとする、真に大規模で複雑なシステムです。



今日は、これらのツールの1つに焦点を当てます。つまり、プログラマーによるツールの使用と実装に焦点を当てます。 完全なドキュメンテーションの欠如、およびロシア語の記事と非常に少数の外国の記事の不足により、フレームワークのこの興味深い機能に関する特定の秘密のベールを明らかにし、このトピックをHabréに関する最初の記事として選択することになりました。



ミドルウェア



この記事では、読者がフレームワークのこの機能の基本的な使用法に既に精通していることを前提としているため、この点については長い間説明しません。


Laravelは、すぐにアプリケーションに着信するHTTPリクエストをフィルタリングするためのかなり強力な機能を提供します。 私たちは皆の愛する(またはそうでない) ミドルウェアについて話している-これらのクラスでは、開発者は公式ドキュメントの「基本」セクションを読みながら、Laravelの出会いをすぐに習得します。これは驚くことではありません-ミドルウェアは主要で最も重要なものの1つですシステム全体が構築されるブリック。



Laravelのこのコンポーネントの標準ユーザーケースの例は、 EncryptCookies / RedirectIfAuthenticated / VerifyCsrfTokenです。カスタム実装の例として、リクエストをさらに転送する前にミドルウェアアプリケーションのローカリゼーション(特定のリクエストデータに基づいて必要なローカリゼーションを設定)を引用できます。



深aへ



みんながここに来ることを願って



さて、主要なポイントが終わりました-私たちは多くの-アルファとオメガ、始まりと終わり-Laravelソースの恐ろしい場所を掘り下げることができます 。 すぐに記事を閉じるために手を差し伸べた人-あなたの時間をかけてください。 実際、このフレームワークのソースコードには、概念的な側面から実際に複雑なものはほとんどありません。作成者は、明確で便利なインターフェイスを作成して頭脳で作業するだけでなく、ソースコードレベルでも同じことを直接しようとしています。しないでください。



コードとロジックのレベルでミドルウェアパイプラインの概念をできるだけシンプルでアクセスしやすいように説明しようとします。そして、記事のフレームワーク内でそれが必要でない場合は、それ以上深く入らないようにします。 したがって、コメント内にすべてのソース行を暗記している人がいる場合は、私の表面的なナレーションを批判しないようにお願いします。 しかし、不正確さの推奨事項と修正は歓迎されます。



ミドルウェア-バリケードを越えて



良い例が提供されていれば、何かを学ぶことは常に簡単だと思います。 したがって、私はあなたと私をこの神秘的な獣をPipelineという名前で研究するように勧めます。 そのような勇敢な男性が本当に存在する場合、さらに読む前に空のLaravelプロジェクトバージョン5.7をインストールする必要があります-バージョンは執筆時点で最後であるという事実のみによるものであり、上記のすべては少なくともバージョン5.4と同一である必要があります 記事の本質と結論を知りたいだけの人は、この部分を安全にスキップできます。



システムにすでに組み込まれている動作を学習することを除いて、コンポーネントの動作を学習するよりも良いことはありますか? たぶん何かできるかもしれませんが、不必要な複雑化をせずに標準のミドルウェア、つまりギャング全体で最もシンプルで最も理解しやすいRedirectIfAuthenticatedで分析を開始します



RedirectIfAuthenticated.php
class RedirectIfAuthenticated { /**      * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string|null $guard * @return mixed */ public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check()) { return redirect('/'); } return $next($request); } }
      
      







古典的なミドルウェアクラスには、リクエストを直接処理し、処理をチェーン内の次のプロセスに渡すメインメソッドがあります。この場合、これはハンドルメソッドです。 この特定のクラスでは、リクエストの処理は非常に単純です-「ユーザーが承認されたら、メインページにリダイレクトして、チェーンを終了します」。



app / Http / Kernel.phpでこのミドルウェアの登録を見ると、「ルートミドルウェア」に登録されていることがわかります。 システムがこのミドルウェアでどのように機能するかを知るために、 アプリ/ Http / Kernelが継承するクラスに行きましょう-Illuminate \ Foundation \ Http \ Kernelクラスから継承します。 この段階で、私たちのフレームワークのソースコードを地獄に 、またはむしろ、その最も重要で主要な部分、つまりHTTPを扱うコアに地獄へと直接ゲートを開きます。 ちなみに、LaravelはSymfonyの多くのコンポーネント、特にこの部分ではHttpFoundationHttpKernelに基づいています。



カーネルコンストラクターでのミドルウェアの定義と実装は次のとおりです。



Illuminate \ Foundation \ Http \ Kernel(アプリケーション$アプリ、ルーター$ルーター)
  /**    HTTP Kernel . * Create a new HTTP kernel instance. * * @param \Illuminate\Contracts\Foundation\Application $app * @param \Illuminate\Routing\Router $router * @return void */ public function __construct(Application $app, Router $router) { $this->app = $app; $this->router = $router; $router->middlewarePriority = $this->middlewarePriority; foreach ($this->middlewareGroups as $key => $middleware) { $router->middlewareGroup($key, $middleware); } foreach ($this->routeMiddleware as $key => $middleware) { $router->aliasMiddleware($key, $middleware); } }
      
      







コードは非常にシンプルで簡単です-配列内の各ミドルウェアに対して、ルーターのエイリアス/インデックスに登録します。 RouteクラスのaliasMiddlewareおよびmiddlewareGroupsメソッドは、ルーターオブジェクトの配列の1つにミドルウェアを追加するだけです。 しかし、これは記事の文脈には含まれていないため、この瞬間をスキップして先に進みます。



私たちが本当に興味を持っているのはsendRequestThroughRouteメソッドです。これは文字通りRoute経由でRequest を送信する方法変換します



Illuminate \ Foundation \ Http \ Kernel :: sendRequestThroughRouter($リクエスト)
  /**     middleware / router. * Send the given request through the middleware / router. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ protected function sendRequestThroughRouter($request) { // *    * return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); }
      
      







パラメータとして、このメソッドはリクエストを受け取ります。 この時点で、 RedirectIfAuthenticatedのコードをもう一度見る必要があります 。 ミドルウェアのhandleメソッドでもリクエストを取得します。このメモは後で必要になります。



上記のコードには、非常に明確で読みやすいインターフェースがあります。 「パイプライン」は、登録された各ミドルウェアを介してリクエストを送信し、それをルーターに「転送」します 。 魅力的で素晴らしい。 この段階では、コードのこのセクションをさらに分解しようとはしないと思います。システム全体におけるこのセクションの役割について簡単に説明します。



コントローラーにリクエストが届く前に、URL自体の単純な解析から始まり、 Requestクラスの初期化で終わる多くのアクションが実行されます。 ミドルウェアもこのアクションチェーンに関与しています。 ミドルウェアクラス自体は、 Chain of ResponsibilityまたはChain of Responsibilityを設計する(ほぼ)パターンを実装するため、各具体的なミドルウェアクラスはチェーン内の単なるリンクになります。



上記では、当初考えられていたRedirectIfAuthenticatedクラスに戻っただけではありません。 リクエストは、ミドルウェアルートに必要なすべてを通過するものを含め、チェーンに沿って「循環」します。 この瞬間は、私たち自身のチェーン内の私たち自身のリンクで作業するのに役立ちます。これについては後で詳しく説明します。



パイプライン-アプリケーションの下水道



上で見たパイプラインの実装の一例。 しかし、この記事の目的は、Laravelとの統合レベルでこのコンポーネントの動作を説明することだけでなく、独自のコードでこのクラスを操作する基本原則を説明することでもありました。



クラス自体は、名前空間を含む完全な定義によって見つけることができます。
イルミネーション\パイプライン\パイプライン


解決する必要がある特定のタスクに応じて、このコンポーネントには非常に多くのアプリケーションがありますが、最も明白な動機の1つは、システム全体のプロセスを妨害せず、ビジネスロジックのレベルで排他的に決定される、独自の要求ハンドラーチェーンを作成する要件です。 また、クラスインターフェイスには十分な抽象化レベルがあり、さまざまなタイプのキューを実装するのに十分な機能があります。



Laravelでのサンプル実装



クエリのリアリティチェーンから最もシンプルかつ最もリモートなものを実現します。 文字列「HELLO WORLD」をデータとして使用し、2つのハンドラーを使用して、文字列「Hello User」を作成します。 コードは意図的に簡素化されています。



独自の「パイプ」をすぐに実装する前に、このパイプの要素を特定する必要があります。 要素はミドルウェアとの類推によって書かれています:



ハンドラーの定義
StrToLowerAction.php:

 use Closure; class StrToLowerAction { /** * Handle an incoming request. * * @param string $content * @param Closure $next * @return mixed */ public function handle(string $content, Closure $next) { $content = strtolower($content); return $next($content); } }
      
      





SetUserAction.php:



 use Closure; class SetUserAction { /** * Handle an incoming request. * * @param string $content * @param Closure $next * @return mixed */ public function handle(string $content, Closure $next) { $content = ucwords(str_replace('world', 'user', $content)); return $next($content); } }
      
      







次に、「パイプライン」を作成し、それを介して送信するデータの種類を決定し、このデータを送信するプロセッサのコレクションを決定し、チェーン全体に渡されたデータを引数として受け取るコールバックを定義します。 チェーン全体のデータが変更されない場合、コールバックのある部分は省略できます。



 $pipes = [ StrToLowerAction::class, SetUserNameAction::class ]; $data = 'Hello world'; $finalData = app(Pipeline::class) ->send($data) // ,       ->through($pipes) //   ->then(function ($changedData) { return $changedData; //  ,    }); var_dump($finalData); //      $finalData
      
      





また、ハンドラーで独自のメソッドを定義する必要がある場合、またはPipelineインターフェイスが特別なvia( 'method_name')メソッド提供する場合、チェーン処理は次のように記述できます。



 $finalData = app(Pipeline::class) ->send($data) ->through($pipes) ->via('handle') //      ,         ->then(function ($changedData) { return $changedData; });
      
      





直接、プロセッサを通過するデータは、プロセッサとのやり取りだけでなく、絶対に任意のものにすることができます。 ヒントを入力し、チェーンで返されるオブジェクトのタイプを設定すると、データの整合性エラーを回避できます。



おわりに



Laravelは多数の組み込みクラスを提供し、それらの多くの柔軟性により、十分にシンプルで複雑なものを開発できます。 この記事では、Laravelに組み込まれたPipelineクラスに基づいて、リクエスト用の単純なキューを作成する可能性を検討しました。 最終コードでのこのクラスの実装は完全に異なる場合があり、このツールの柔軟性により、特定のアルゴリズムを構築する際に多くの不要なアクションを取り除くことができます。



フレームワークのこの機能を具体的に使用する方法は、割り当てられたタスクによって異なります。



All Articles