Características de Middleware y Pipeline en Laravel



Laravel es un sistema realmente grande y complejo que intenta resolver la mayoría de las tareas cotidianas de un desarrollador web de la manera más elegante y recolectar tantas herramientas como sea posible y, lo que es más importante, con la mayor interfaz humana posible.

Y hoy nos centraremos en una de estas herramientas, o más bien en su uso e implementación por parte del programador. La falta de documentación completa, así como la falta de artículos en ruso y un número muy pequeño de artículos extranjeros, me impulsaron a decidir revelar un cierto velo de secreto sobre esta característica interesante del marco y elegir este tema como mi primer artículo sobre Habré.

Middleware


Este artículo supone que el lector ya está familiarizado con el uso básico de esta funcionalidad del marco, por lo que no me detendré en este punto durante mucho tiempo.

Fuera de la caja, Laravel nos proporciona una funcionalidad bastante poderosa para filtrar las solicitudes HTTP entrantes a nuestra aplicación. Estamos hablando del Middleware querido (o no) de todos: el desarrollador se encuentra rápidamente con estas clases en el camino hacia el dominio de Laravel, mientras lee la sección "Lo básico" de la documentación oficial, y esto no es sorprendente: Middleware es uno de los principales y más importantes ladrillos sobre la base de los cuales se construye todo el sistema.

Ejemplos de casos de usuario estándar de este componente en Laravel son: EncryptCookies / RedirectIfAuthenticated / VerifyCsrfToken , y como ejemplo de una implementación personalizada, puede citar la localización de la aplicación de middleware (configurando la localización requerida en función de ciertos datos de solicitud) antes de transferir la solicitud.

Más profundo en el abismo


Renunciar a la esperanza de que todos vengan aquí


Bueno, ahora que los puntos principales han terminado, podemos profundizar en un lugar terrible para muchos, en alfa y omega, el principio y el final, en las fuentes de Laravel . Aquellos que se acercaron para cerrar el artículo inmediatamente, tómese su tiempo. De hecho, en el código fuente de este marco no hay casi nada realmente complicado desde el punto de vista conceptual: los creadores claramente están tratando no solo de crear una interfaz clara y conveniente para trabajar con su creación, sino que también están tratando de hacer lo mismo directamente en el nivel del código fuente, lo que no puede no por favor

Trataré de explicar el concepto de cómo Middleware y Pipeline funcionan a nivel de código y lógica de la manera más simple y accesible posible, e intentaré no entrar en él, donde no es necesario en el marco del artículo. Entonces, si hay personas en los comentarios que conocen todas las líneas de origen de memoria, le pido que se abstenga de criticar mi narración superficial. Pero cualquier recomendación y corrección de imprecisiones son bienvenidas.

Middleware - a través de las barricadas


Creo que aprender cualquier cosa siempre es más fácil cuando se proporcionan buenos ejemplos. Por lo tanto, los invito a estudiar a esta misteriosa bestia bajo el nombre de Pipeline . Si tales hombres valientes realmente existen, entonces antes de seguir leyendo tendremos que instalar un proyecto vacío de Laravel versión 5.7: la versión se debe solo al hecho de que es la última en el momento de la escritura, todo lo anterior debe ser idéntico al menos a la versión 5.4. Aquellos que solo quieran conocer la esencia y las conclusiones del artículo pueden saltarse esta parte de manera segura.

¿Qué podría ser mejor que estudiar el comportamiento de un componente, excepto estudiar el comportamiento ya integrado en el sistema? Tal vez algo pueda, pero lo haremos sin complicaciones innecesarias y comenzaremos nuestro análisis con el Middleware estándar, es decir, con el más simple y comprensible de toda la pandilla: 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); } } 


En cualquier clase de middleware clásico, hay un método principal que debe procesar directamente la solicitud y pasar el procesamiento al siguiente en la cadena; en nuestro caso, este es el método de manejo . En esta clase en particular, el procesamiento de la solicitud es bastante simple: "si el usuario está autorizado, rediríjalo a la página principal y, por lo tanto, finalice la cadena".

Si observamos el registro de este Middleware en la aplicación / Http / Kernel.php , veremos que está registrado en la 'ruta del middleware'. Para que podamos descubrir cómo funciona el sistema con este middleware, vayamos a la clase de la que hereda nuestra aplicación / Http / Kernel , y hereda de la clase Illuminate \ Foundation \ Http \ Kernel . En esta etapa, abriremos directamente las puertas al infierno del código fuente de nuestro marco, o más bien, a la parte más importante y principal de él, al núcleo del trabajo con HTTP. Por cierto, a quién le importa: Laravel se basa en muchos componentes de Symfony , específicamente en esta parte, en HttpFoundation y HttpKernel .

La definición e implementación de nuestro middleware en el constructor del kernel es la siguiente:

Illuminate \ Foundation \ Http \ Kernel (Aplicación $ 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); } } 


El código es bastante simple y directo: para cada middleware de la matriz, lo registramos con un alias / índice en nuestro enrutador. Los métodos aliasMiddleware y middlewareGroups de nuestra clase Route solo están agregando middleware a una de las matrices del objeto enrutador. Pero esto no está incluido en el contexto del artículo, por lo que omitiremos este momento y seguiremos adelante.

Lo que realmente nos interesa es el método sendRequestThroughRoute , que literalmente traduce cómo enviar una solicitud a través de la ruta :

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


Como parámetro, este método recibe una solicitud. En este punto, deberíamos volver a mirar nuestro código RedirectIfAuthenticated . También recibimos una solicitud en el método de manejo de nuestro middleware, necesitaremos esta nota un poco más tarde.

El código anterior tiene una interfaz muy clara y fácil de leer: "Pipeline", que envía una solicitud a través de cada uno de los middleware registrados y luego la "transfiere" al enrutador . Encantador y maravilloso. Creo que en esta etapa no intentaremos descomponer más esta sección de código, solo describiré brevemente el papel de esta sección en todo el sistema:

Antes de que una solicitud ingrese a su controlador, se realizan muchas acciones, desde el simple análisis de la propia URL hasta la inicialización de la clase Solicitud . Middleware también está involucrado en esta cadena de acción. Las clases de middleware implementan el patrón de diseño (casi) de la Cadena de Responsabilidad , o Cadena de Responsabilidad , por lo que cada clase de midleware concreta es solo un eslabón en la cadena.

Arriba, no acabamos de regresar a nuestra clase RedirectIfAuthenticated originalmente considerada. La solicitud está "circulando" a lo largo de la cadena, incluida la que pasa por todo lo necesario para la ruta del middleware. Este momento nos ayudará a trabajar con nuestros propios enlaces en nuestra propia cadena, más sobre eso más adelante.

Pipeline - Alcantarillado de nuestra aplicación


Un ejemplo de la implementación de Pipeline que vimos anteriormente. Pero el propósito del artículo no era solo explicar el funcionamiento de este componente a nivel de integración con Laravel, sino también explicar el principio básico de trabajar con esta clase en nuestro propio código.

La clase en sí se puede encontrar por su definición completa con espacio de nombres:
Iluminar \ Pipeline \ Pipeline

Puede haber muchas aplicaciones para este componente, dependiendo de la tarea específica que necesite resolver, pero una de las motivaciones más obvias es el requisito de crear su propia cadena de controladores de solicitudes, que no interfiere con los procesos de todo el sistema y se determina exclusivamente al nivel de su lógica empresarial. Además, la interfaz de clase tiene un nivel suficiente de abstracción y tiene suficiente funcionalidad para implementar diferentes tipos de colas.

Implementación de muestra en Laravel


Implementamos la cadena de consulta más simple y remota de la realidad. Usaremos la cadena "HOLA MUNDO" como datos, y con la ayuda de dos controladores formaremos la cadena "Hola usuario" a partir de ella. El código se simplifica intencionalmente.

Antes de la implementación inmediata de nuestra propia "tubería", necesitamos identificar los elementos de esta tubería. Los elementos se escriben por analogía con middleware:

Definiendo manejadores
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); } } 


Luego creamos una "tubería", determinamos qué tipo de datos queremos enviar, determinamos a través de qué colección de procesadores queremos enviar estos datos, y también definimos una devolución de llamada, que recibe como argumento que nuestros datos pasaron por toda la cadena. En el caso de que los datos en toda la cadena permanezcan sin cambios, la parte con devolución de llamada se puede omitir:

 $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 

Además, si desea o necesita definir su propio método en los controladores, la interfaz Pipeline proporciona un método especial vía ('nombre_método') , entonces el procesamiento en cadena se puede escribir de esta manera:

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

Directamente, los datos que pasamos a través de los procesadores pueden ser absolutamente cualquier cosa, así como la interacción con ellos. Escribir sugerencias y establecer el tipo del objeto devuelto en la cadena ayudará a evitar errores de integridad de datos.

Conclusión


Laravel proporciona una gran cantidad de clases integradas, y la flexibilidad de muchas de ellas nos permite desarrollar algo complejo con suficiente simplicidad. Este artículo examinó la posibilidad de crear colas simples para solicitudes basadas en la clase Pipeline integrada en Laravel. Las implementaciones de esta clase en el código final pueden ser completamente diferentes, y la flexibilidad de esta herramienta le permite deshacerse de muchas acciones innecesarias al construir ciertos algoritmos.

El uso específico de esta función del marco depende de las tareas que se le asignen.

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


All Articles