Middleware- und Pipeline-Funktionen in Laravel



Laravel ist ein wirklich großes und komplexes System, das versucht, die meisten alltäglichen Aufgaben eines Webentwicklers auf eleganteste Weise zu lösen und so viele Tools wie möglich und vor allem mit so vielen Benutzeroberflächen wie möglich zu sammeln.

Und heute konzentrieren wir uns auf eines dieser Tools bzw. auf dessen Verwendung und Implementierung durch den Programmierer. Das Fehlen einer vollständigen Dokumentation sowie das Fehlen russischsprachiger Artikel und einer sehr geringen Anzahl ausländischer Artikel veranlasste mich, einen gewissen Schleier der Geheimhaltung über dieses interessante Merkmal des Frameworks zu enthüllen und dieses Thema als meinen ersten Artikel über Habré zu wählen.

Middleware


In diesem Artikel wird davon ausgegangen, dass der Leser bereits mit der grundlegenden Verwendung dieser Funktionalität des Frameworks vertraut ist, sodass ich auf diesen Punkt nicht lange eingehen werde.

Laravel bietet uns sofort eine ziemlich leistungsstarke Funktionalität zum Filtern eingehender HTTP-Anforderungen an unsere Anwendung. Wir sprechen über die geliebte (oder nicht geliebte) Middleware aller - der Entwickler begegnet diesen Klassen auf dem Weg zur Beherrschung von Laravel ziemlich schnell, während er den Abschnitt "Die Grundlagen" der offiziellen Dokumentation liest, und dies ist nicht überraschend - Middleware ist eine der wichtigsten und wichtigsten Ziegel, auf deren Grundlage das gesamte System aufgebaut ist.

Beispiele für Standardbenutzerfälle dieser Komponente in Laravel sind: EncryptCookies / RedirectIfAuthenticated / VerifyCsrfToken . Als Beispiel für eine benutzerdefinierte Implementierung können Sie die Lokalisierung von Middleware-Anwendungen (Festlegen der erforderlichen Lokalisierung basierend auf bestimmten Anforderungsdaten) angeben , bevor Sie die Anforderung weiter übertragen.

Tiefer in den Abgrund


Gib die Hoffnung auf, dass alle hier reinkommen


Nun, da die Hauptpunkte vorbei sind, können wir in den Laravel-Quellen für viele in einen schrecklichen Ort eintauchen - in Alpha und Omega, Anfang und Ende. Diejenigen, die den Artikel sofort schließen wollten, nehmen sich Zeit. Tatsächlich gibt es im Quellcode dieses Frameworks konzeptionell fast nichts wirklich Kompliziertes - die Entwickler versuchen eindeutig nicht nur, eine klare und bequeme Schnittstelle für die Arbeit mit ihrer Idee zu schaffen, sondern sie versuchen auch, dies direkt auf der Ebene des Quellcodes zu tun, was nicht möglich ist nicht zu gefallen.

Ich werde versuchen, das Konzept der Funktionsweise von Middleware und Pipeline auf der Ebene von Code und Logik so einfach und zugänglich wie möglich zu erläutern, und ich werde versuchen, nicht darauf einzugehen - wo dies im Rahmen des Artikels nicht erforderlich ist. Wenn es also in den Kommentaren Leute gibt, die alle Zeilen des Quellcodes auswendig können, bitte ich Sie, meine oberflächliche Erzählung nicht zu kritisieren. Empfehlungen und Korrekturen von Ungenauigkeiten sind jedoch nur willkommen.

Middleware - über die Barrikaden


Ich glaube, dass es immer einfacher ist, etwas zu lernen, wenn gute Beispiele geliefert werden. Deshalb lade ich Sie und mich ein, dieses mysteriöse Tier unter dem Namen Pipeline zu studieren. Wenn solche tapferen Männer wirklich existieren, müssen wir vor dem weiteren Lesen ein leeres Laravel-Projekt Version 5.7 installieren - die Version ist nur darauf zurückzuführen, dass es zum Zeitpunkt des Schreibens die letzte ist. Alle oben genannten sollten mindestens mit Version 5.4 identisch sein. Diejenigen, die nur das Wesentliche und die Schlussfolgerungen des Artikels kennen wollen, können diesen Teil sicher überspringen.

Was gibt es Schöneres, als das Verhalten einer Komponente zu untersuchen, als das bereits in das System integrierte Verhalten zu untersuchen? Vielleicht kann etwas, aber wir werden ohne unnötige Komplikationen auskommen und unsere Analyse mit der Standard-Middleware beginnen - nämlich mit der einfachsten und verständlichsten der gesamten Bande - 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); } } 


In jeder klassischen Middleware-Klasse gibt es eine Hauptmethode, die die Anforderung direkt verarbeiten und die Verarbeitung an die nächste in der Kette übergeben muss - in unserem Fall ist dies die Handle- Methode. In dieser bestimmten Klasse ist die Verarbeitung der Anforderung recht einfach: "Wenn der Benutzer autorisiert ist, leiten Sie ihn zur Hauptseite weiter und beenden Sie dadurch die Kette."

Wenn wir uns die Registrierung dieser Middleware in app / Http / Kernel.php ansehen , werden wir sehen, dass sie in der 'Route Middleware' registriert ist. Um herauszufinden, wie das System mit dieser Middleware funktioniert, gehen wir zu der Klasse, von der unsere App / Http / Kernel erbt - und die von der Klasse Illuminate \ Foundation \ Http \ Kernel erbt. In dieser Phase öffnen wir direkt die Tore zur Hölle des Quellcodes unseres Frameworks oder vielmehr zum wichtigsten und wichtigsten Teil davon - zum Kern der Arbeit mit HTTP. Übrigens, wen interessiert das schon - Laravel basiert auf vielen Komponenten von Symfony , speziell in diesem Teil - auf HttpFoundation und HttpKernel .

Die Definition und Implementierung unserer Middleware im Kernel-Konstruktor lautet wie folgt:

Illuminate \ Foundation \ Http \ Kernel (Anwendung $ app, Router $ router)
  /**    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); } } 


Der Code ist recht einfach und unkompliziert - für jede Middleware im Array registrieren wir ihn mit einem Alias ​​/ Index in unserem Router. Die Methoden aliasMiddleware und middlewareGroups unserer Route-Klasse fügen lediglich Middleware zu einem der Arrays des Router-Objekts hinzu. Dies ist jedoch nicht im Kontext des Artikels enthalten, daher werden wir diesen Moment überspringen und fortfahren.

Was uns wirklich interessiert, ist die sendRequestThroughRoute- Methode, die wörtlich übersetzt, wie eine Anfrage über die Route gesendet wird:

Illuminate \ Foundation \ Http \ Kernel :: sendRequestThroughRouter ($ request)
  /**     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()); } 


Als Parameter empfängt diese Methode eine Anfrage. An dieser Stelle sollten wir uns noch einmal unseren RedirectIfAuthenticated- Code ansehen. Wir erhalten auch eine Anfrage in der Handle- Methode unserer Middleware, wir werden diesen Hinweis etwas später benötigen.

Der obige Code hat eine sehr klare und lesbare Schnittstelle - "Pipeline", die eine Anfrage über jede der registrierten Middleware sendet und diese dann an den Router "überträgt" . Charmant und wunderbar. Ich denke, in diesem Stadium werden wir nicht versuchen, diesen Codeabschnitt weiter zu zerlegen. Ich werde die Rolle dieses Abschnitts im gesamten System nur kurz beschreiben:

Bevor eine Anforderung in Ihren Controller eingeht, werden viele Aktionen ausgeführt, von der einfachen Analyse der URL selbst bis zur Initialisierung der Anforderungsklasse . Middleware ist ebenfalls an dieser Aktionskette beteiligt. Die Middleware-Klassen selbst implementieren das (fast) Muster des Entwurfs der Verantwortungskette oder der Verantwortungskette , sodass jede einzelne Midleware-Klasse nur ein Glied in der Kette ist.

Oben sind wir nicht nur zu unserer ursprünglich betrachteten RedirectIfAuthenticated- Klasse zurückgekehrt. Die Anfrage "zirkuliert" entlang der Kette, einschließlich derjenigen, die alles durchläuft, was für die Middleware-Route erforderlich ist. Dieser Moment wird uns helfen, mit unseren eigenen Gliedern in unserer eigenen Kette zu arbeiten, dazu später mehr.

Pipeline - Channeling unserer Anwendung


Ein Beispiel für die Implementierung der Pipeline haben wir oben gesehen. Der Zweck des Artikels bestand jedoch nicht nur darin, die Funktionsweise dieser Komponente auf der Ebene der Integration mit Laravel zu erläutern, sondern auch das Grundprinzip der Arbeit mit dieser Klasse in unserem eigenen Code zu erläutern.

Die Klasse selbst kann durch ihre vollständige Definition mit Namespace gefunden werden:
Beleuchten Sie \ Pipeline \ Pipeline

Abhängig von der spezifischen Aufgabe, die Sie lösen müssen, kann es für diese Komponente eine Vielzahl von Anwendungen geben. Eine der offensichtlichsten Motivationen ist jedoch die Anforderung, eine eigene Kette von Anforderungsbearbeitern zu erstellen, die die Prozesse des gesamten Systems nicht beeinträchtigt und ausschließlich auf der Ebene Ihrer Geschäftslogik festgelegt wird. Außerdem verfügt die Klassenschnittstelle über einen ausreichenden Abstraktionsgrad und eine ausreichende Funktionalität, um verschiedene Arten von Warteschlangen zu implementieren.

Beispielimplementierung in Laravel


Wir implementieren die einfachste und realitätsfernste Abfragekette. Wir werden die Zeichenfolge "HELLO WORLD" als Daten verwenden und mit Hilfe von zwei Handlern die Zeichenfolge "Hello User" daraus bilden. Der Code wird absichtlich vereinfacht.

Vor der sofortigen Implementierung unserer eigenen „Pipe“ müssen wir die Elemente dieser Pipe identifizieren. Elemente werden analog zur Middleware geschrieben:

Handler definieren
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); } } 


Dann erstellen wir eine „Pipeline“, bestimmen, welche Art von Daten wir über sie senden möchten, bestimmen, über welche Sammlung von Prozessoren wir diese Daten senden möchten, und definieren auch einen Rückruf, der als Argument unsere Daten empfängt, die durch die gesamte Kette geleitet werden. Für den Fall, dass die Daten in der gesamten Kette unverändert bleiben, kann der Teil mit Rückruf weggelassen werden:

 $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 

Wenn Sie Ihre eigene Methode in Handlern definieren möchten oder müssen, bietet die Pipeline-Schnittstelle eine spezielle Methode via ('method_name'). Die Kettenverarbeitung kann folgendermaßen geschrieben werden:

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

Direkt können die Daten, die wir durch die Prozessoren weitergeben, absolut alles sein, ebenso wie die Interaktion mit ihnen. Durch Eingabe von Hinweisen und Festlegen des Typs des zurückgegebenen Objekts in der Kette können Datenintegritätsfehler vermieden werden.

Fazit


Laravel bietet eine große Anzahl integrierter Klassen, und die Flexibilität vieler von ihnen ermöglicht es uns, etwas Komplexes mit ausreichender Einfachheit zu entwickeln. In diesem Artikel wurde die Möglichkeit untersucht, einfache Warteschlangen für Anforderungen basierend auf der in Laravel integrierten Pipeline-Klasse zu erstellen. Die Implementierungen dieser Klasse im endgültigen Code können völlig unterschiedlich sein. Dank der Flexibilität dieses Tools können Sie beim Erstellen bestimmter Algorithmen viele unnötige Aktionen vermeiden.

Wie Sie diese Funktion des Frameworks speziell verwenden, hängt von den Ihnen zugewiesenen Aufgaben ab.

Source: https://habr.com/ru/post/de429214/


All Articles