- Introducción
- Definiendo Middleware
- Registrando Middleware
- Parámetros de Middleware
- Middleware Terminables
#Introducción
Middleware proporciona un mecanismo conveniente para inspeccionar y filtrar las solicitudes HTTP que entran a su aplicación. Por ejemplo, Laravel incluye un middleware que verifica que el usuario de su aplicación esté autenticado. Si el usuario no está autenticado, el middleware redirigirá al usuario a la pantalla de inicio de sesión de su aplicación. Sin embargo, si el usuario está autenticado, el middleware permitirá que la solicitud continúe dentro de 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 su aplicación. Hay varios middleware incluidos en el framework Laravel, incluyendo middleware para autenticación y protección CSRF. Todos estos middleware se encuentran en el directorio app/Http/Middleware.
#Definiendo Middleware
Para crear un nuevo middleware, use el comando Artisan make:middleware:
php artisan make:middleware EnsureTokenIsValid
Este comando colocará una nueva clase EnsureTokenIsValid dentro de su directorio app/Http/Middleware. En este middleware, solo permitiremos el acceso a la ruta si el valor del parámetro token coincide con un valor especificado. De lo contrario, redirigiremos a los usuarios de vuelta 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 puede ver, si el token proporcionado 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 adentro de la aplicación (permitiendo que el middleware "pase"), debe llamar al callback $next con la $request.
Es mejor imaginar middleware como una serie de "capas" que las solicitudes HTTP deben atravesar antes de llegar a su aplicación. Cada capa puede examinar la solicitud e incluso rechazarla por completo.
Todos los middleware se resuelven a través del service container, por lo que puede usar type-hint para cualquier dependencia que necesite 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 aplicación maneje la solicitud:
<?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 aplicación maneje la solicitud:
<?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;
}
}
#Registrando Middleware
#Middleware Global
Si desea que un middleware se ejecute durante cada solicitud HTTP a su aplicación, liste la clase middleware en la propiedad $middleware de su clase app/Http/Kernel.php.
#Asignando Middleware a Rutas
Si desea asignar middleware a rutas específicas, puede invocar el método middleware al definir la ruta:
use App\Http\Middleware\Authenticate;
Route::get('/profile', function () {
// ...
})->middleware(Authenticate::class);
Puede asignar múltiples middleware a la ruta pasando un arreglo de nombres de middleware al método middleware:
Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);
Para mayor comodidad, puede asignar alias a middleware en el archivo app/Http/Kernel.php de su aplicación. Por defecto, la propiedad $middlewareAliases de esta clase contiene entradas para los middleware incluidos con Laravel. Puede agregar sus propios middleware a esta lista y asignarles un alias de su elección:
// Dentro de la clase App\Http\Kernel...
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 el alias de middleware ha sido definido en el kernel HTTP, puede usar el alias al asignar middleware a rutas:
Route::get('/profile', function () {
// ...
})->middleware('auth');
#Excluyendo Middleware
Al asignar middleware a un grupo de rutas, ocasionalmente puede necesitar evitar que el middleware se aplique a una ruta individual dentro del grupo. Puede lograr esto usando 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 puede excluir un conjunto dado de middleware de un grupo completo de definiciones de rutas:
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 a middleware global.
#Grupos de Middleware
A veces puede querer agrupar varios middleware bajo una sola clave para facilitar su asignación a rutas. Puede lograr esto usando la propiedad $middlewareGroups de su kernel HTTP.
Laravel incluye grupos de middleware predefinidos web y api que contienen middleware comunes que puede querer aplicar a sus rutas web y API. Recuerde, estos grupos de middleware se aplican automáticamente por el proveedor de servicios App\Providers\RouteServiceProvider de su aplicación a las rutas dentro de sus archivos de rutas correspondientes web y api:
/**
* Los grupos de middleware de rutas 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 pueden asignarse a rutas y acciones de controladores usando la misma sintaxis que para middleware individuales. Nuevamente, los grupos de middleware facilitan asignar muchos middleware a una ruta a la vez:
Route::get('/', function () {
// ...
})->middleware('web');
Route::middleware(['web'])->group(function () {
// ...
});
Por defecto, los grupos de middleware web y api se aplican automáticamente a los archivos correspondientes routes/web.php y routes/api.php de su aplicación por el App\Providers\RouteServiceProvider.
#Ordenando Middleware
Raramente, puede necesitar que sus middleware se ejecuten en un orden específico pero no tener control sobre su orden cuando se asignan a la ruta. En este caso, puede especificar la prioridad de sus middleware usando la propiedad $middlewarePriority en su archivo app/Http/Kernel.php. Esta propiedad puede no existir en su kernel HTTP por defecto. Si no existe, puede copiar su definición predeterminada a continuación:
/**
* La lista de middleware ordenada por prioridad.
*
* Esto fuerza que los middleware no globales siempre estén en el orden dado.
*
* @var string[]
*/
protected $middlewarePriority = [
\Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
\Illuminate\Cookie\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::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
Los middleware también pueden recibir parámetros adicionales. Por ejemplo, si su aplicación necesita verificar que el usuario autenticado tenga un "rol" dado antes de realizar una acción, podría crear un middleware EnsureUserHasRole que reciba un nombre de rol como 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 de middleware pueden especificarse al definir la ruta separando el nombre del middleware y los parámetros con un ::
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware('role:editor');
Se pueden delimitar múltiples parámetros con comas:
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware('role:editor,publisher');
#Middleware Terminables
A veces un middleware puede necesitar realizar alguna tarea después de que la respuesta HTTP ha sido enviada al navegador. Si define un método terminate en su middleware y su servidor web usa FastCGI, el método terminate será llamado automáticamente después de que la respuesta sea enviada 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 ha sido enviada al navegador.
*/
public function terminate(Request $request, Response $response): void
{
// ...
}
}
El método terminate debe recibir tanto la solicitud como la respuesta. Una vez que haya definido un middleware terminable, debe agregarlo a la lista de middleware de rutas o globales en el archivo app/Http/Kernel.php.
Al llamar al método terminate en su middleware, Laravel resolverá una instancia nueva del middleware desde el service container. Si desea usar la misma instancia de middleware cuando se llamen los métodos handle y terminate, registre el middleware en el container usando el método singleton del container. Normalmente esto debe hacerse en el método register de su AppServiceProvider:
use App\Http\Middleware\TerminatingMiddleware;
/**
* Registrar cualquier servicio de la aplicación.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}