1. Conceptos básicos
  2. Middleware

Introducción

Los middleware proporcionan un mecanismo conveniente para inspeccionar y filtrar las solicitudes HTTP que ingresan a tu aplicación. Por ejemplo, Laravel incluye un middleware que verifica si el usuario de tu aplicación está autenticado. Si el usuario no está autenticado, el middleware redirigirá al usuario a la pantalla de inicio de sesión de tu aplicación. Sin embargo, si el usuario está autenticado, el middleware permitirá que la solicitud continúe hacia la aplicación.

Se pueden escribir middleware adicionales para realizar una variedad de tareas además de la autenticación. Por ejemplo, un middleware de registro podría registrar todas las solicitudes entrantes a tu aplicación. Hay varios middleware incluidos en el marco de Laravel, incluidos middleware para autenticación y protección CSRF. Todos estos middleware se encuentran en el directorio app/Http/Middleware.

Definir Middleware

Para crear un nuevo middleware, usa el comando Artisan make:middleware:

php artisan make:middleware EnsureTokenIsValid

Este comando colocará una nueva clase EnsureTokenIsValid dentro de tu directorio app/Http/Middleware. En este middleware, solo permitiremos el acceso a la ruta si el valor proporcionado para token coincide con un valor especificado. De lo contrario, redirigiremos a los usuarios de nuevo a la URI home:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class EnsureTokenIsValid
{
/**
* Manejar una solicitud entrante.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->input('token') !== 'my-secret-token') {
return redirect('home');
}
 
return $next($request);
}
}

Como puedes ver, si el token dado no coincide con nuestro token secreto, el middleware devolverá una redirección HTTP al cliente; de lo contrario, la solicitud se pasará más profundamente en la aplicación. Para pasar la solicitud más profundamente en la aplicación (permitiendo que el middleware "pase"), debes llamar al callback $next con la $request.

Es mejor imaginar los middleware como una serie de "capas" que las solicitudes HTTP deben atravesar antes de llegar a tu aplicación. Cada capa puede examinar la solicitud e incluso rechazarla por completo.

Nota Todos los middleware se resuelven a través del contenedor de servicios, por lo que puedes hacer type-hint de cualquier dependencia que necesites dentro del constructor de un middleware.

Middleware y Respuestas

Por supuesto, un middleware puede realizar tareas antes o después de pasar la solicitud más profundamente en la aplicación. Por ejemplo, el siguiente middleware realizaría alguna tarea antes de que la solicitud sea manejada por la aplicación:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class BeforeMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// Realizar acción
 
return $next($request);
}
}

Sin embargo, este middleware realizaría su tarea después de que la solicitud sea manejada por la aplicación:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class AfterMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
 
// Realizar acción
 
return $response;
}
}

Registrar Middleware

Middleware Global

Si deseas que un middleware se ejecute durante cada solicitud HTTP a tu aplicación, lista la clase del middleware en la propiedad $middleware de tu clase app/Http/Kernel.php.

Asignar Middleware a Rutas

Si deseas asignar middleware a rutas específicas, puedes invocar el método middleware al definir la ruta:

use App\Http\Middleware\Authenticate;
 
Route::get('/profile', function () {
// ...
})->middleware(Authenticate::class);

Puedes asignar varios middleware a la ruta pasando un array de nombres de middleware al método middleware:

Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);

Para mayor comodidad, puedes asignar alias a los middleware en el archivo app/Http/Kernel.php de tu aplicación. Por defecto, la propiedad $middlewareAliases de esta clase contiene entradas para los middleware incluidos con Laravel. Puedes agregar tu propio middleware a esta lista y asignarle un alias de tu elección:

// Within App\Http\Kernel class...
 
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

Una vez que se haya definido el alias de middleware en el kernel HTTP, puedes usar el alias al asignar middleware a rutas:

Route::get('/profile', function () {
// ...
})->middleware('auth');

Excluir Middleware

Al asignar middleware a un grupo de rutas, ocasionalmente puedes necesitar evitar que el middleware se aplique a una ruta individual dentro del grupo. Puedes lograr esto utilizando el método withoutMiddleware:

use App\Http\Middleware\EnsureTokenIsValid;
 
Route::middleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/', function () {
// ...
});
 
Route::get('/profile', function () {
// ...
})->withoutMiddleware([EnsureTokenIsValid::class]);
});

También puedes excluir un conjunto dado de middleware de todo un grupo de definiciones de ruta:

use App\Http\Middleware\EnsureTokenIsValid;
 
Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/profile', function () {
// ...
});
});

El método withoutMiddleware solo puede eliminar middleware de ruta y no se aplica al middleware global.

Grupos de Middleware

A veces puedes querer agrupar varios middleware bajo una sola clave para facilitar su asignación a rutas. Puedes lograr esto usando la propiedad $middlewareGroups de tu kernel HTTP.

Laravel incluye los grupos de middleware predefinidos web y api que contienen middleware comunes que es posible que desees aplicar a tus rutas web y API. Recuerda, estos grupos de middleware se aplican automáticamente mediante el proveedor de servicios App\Providers\RouteServiceProvider de tu aplicación a rutas dentro de tus archivos de ruta web y api correspondientes:

/**
* Los grupos de middleware de ruta de la aplicación.
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
 
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];

Los grupos de middleware se pueden asignar a rutas y acciones de controladores utilizando la misma sintaxis que un middleware individual. Nuevamente, los grupos de middleware hacen que sea más conveniente asignar muchos middleware a una ruta de una vez:

Route::get('/', function () {
// ...
})->middleware('web');
 
Route::middleware(['web'])->group(function () {
// ...
});

Nota De forma predeterminada, los grupos de middleware web y api se aplican automáticamente a los archivos correspondientes de rutas routes/web.php y routes/api.php de tu aplicación mediante App\Providers\RouteServiceProvider.

Ordenar Middleware

Raramente, es posible que necesites que tu middleware se ejecute en un orden específico, pero no tener control sobre su orden cuando se asignan a la ruta. En este caso, puedes especificar la prioridad de tu middleware usando la propiedad $middlewarePriority de tu archivo app/Http/Kernel.php. Esta propiedad puede no existir en tu kernel HTTP de forma predeterminada. Si no existe, puedes copiar su definición predeterminada a continuación:

/**
* La lista de middleware ordenada por prioridad.
*
* Esto fuerza a que el middleware no global siempre esté en el orden dado.
*
* @var string[]
*/
protected $middlewarePriority = [
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
\Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];

Parámetros de Middleware

El middleware también puede recibir parámetros adicionales. Por ejemplo, si tu aplicación necesita verificar que el usuario autenticado tiene un "rol" específico antes de realizar una acción determinada, podrías crear un middleware EnsureUserHasRole que reciba el nombre del rol como un argumento adicional.

Los parámetros adicionales del middleware se pasarán al middleware después del argumento $next:

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class EnsureUserHasRole
{
/**
* Manejar una solicitud entrante.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string $role): Response
{
if (! $request->user()->hasRole($role)) {
// Redirigir...
}
 
return $next($request);
}
 
}

Los parámetros del middleware se pueden especificar al definir la ruta separando el nombre del middleware y los parámetros con :. Los múltiples parámetros deben estar delimitados por comas:

Route::put('/post/{id}', function (string $id) {
// ...
})->middleware('role:editor');

Middleware finalizable

A veces, un middleware puede necesitar hacer algún trabajo después de que la respuesta HTTP se ha enviado al navegador. Si defines un método terminate en tu middleware y tu servidor web está utilizando FastCGI, el método terminate se llamará automáticamente después de que se envíe la respuesta al navegador:

<?php
 
namespace Illuminate\Session\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class TerminatingMiddleware
{
/**
* Manejar una solicitud entrante.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
return $next($request);
}
 
/**
* Manejar tareas después de que la respuesta se ha enviado al navegador.
*/
public function terminate(Request $request, Response $response): void
{
// ...
}
}

El método terminate debería recibir tanto la solicitud como la respuesta. Una vez que hayas definido un middleware finalizable, deberías agregarlo a la lista de rutas o middleware global en el archivo app/Http/Kernel.php.

Cuando llamas al método terminate en tu middleware, Laravel resolverá una nueva instancia fresca del middleware desde el contenedor de servicios. Si deseas usar la misma instancia de middleware cuando se llaman los métodos handle y terminate, registra el middleware en el contenedor usando el método singleton del contenedor. Normalmente, esto se debe hacer en el método register de tu AppServiceProvider:

use App\Http\Middleware\TerminatingMiddleware;
 
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}