Introducción

Los middleware proporcionan un mecanismo conveniente para filtrar solicitudes HTTP entrantes 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 redireccionará al usuario a la pantalla de inicio de sesión. Sin embargo, si el usuario es autenticado, el middleware permitirá que la solicitud proceda dentro de la aplicación.

Los middleware adicionales pueden ser escritos para realizar una variedad de tareas además de autenticar. Un middleware de CORS podría ser responsable de agregar los encabezados apropiados para todas las respuestas que va dejando tu aplicación. Un middleware de registro podría registrar todas las solicitudes entrantes en tu aplicación.

Hay varios middleware incluidos en el framework Laravel, incluyendo middleware para autenticación y protección CSRF. Todos esos middleware están localizados en el directorio app/Http/Middleware.

Definiendo un middleware

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

php artisan make:middleware CheckAge

Este comando ubicará una nueva clase CheckAge dentro de tu directorio app/Http/Middleware. En este middleware, nosotros solo permitiremos el acceso a la ruta si la age (edad) suministrada es mayor que 200. De otra forma, redireccionaremos a los usuarios de vuelta a la URL home:

<?php

namespace App\Http\Middleware;

use Closure;

class CheckAge
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->age <= 200) {
            return redirect('home');
        }

        return $next($request);
    }
}

Como puedes ver, si la age (edad) dada es menor o igual a 200, el middleware retornará una redirección HTTP al cliente; de otra forma, la solicitud pasará más adentro de la aplicación. Para pasar la solicitud más profundo dentro de la aplicación (permitiendo al middleware «pasar») llama al callback $next con el $request.

Es mejor visualizar el middleware como una serie de «capas» que deben pasar las solicitudes HTTP antes de que lleguen a tu aplicación. Cada capa puede examinar la solicitud e incluso rechazarla por completo.

Todos los middleware son resueltos a través del contenedor de servicio, de esta forma, puedes declarar el tipo de cualquier dependencia que necesites dentro del constructor del middleware.

Middleware before y after

Que un middleware se ejecute antes o después de una solicitud depende del middleware en sí mismo. Por ejemplo, el siguiente middleware podría realizar alguna tarea antes que la solicitud sea manejada por la aplicación:

<?php

namespace App\Http\Middleware;

use Closure;

class BeforeMiddleware
{
    public function handle($request, Closure $next)
    {
        // Perform action

        return $next($request);
    }
}

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

<?php

namespace App\Http\Middleware;

use Closure;

class AfterMiddleware
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        // Perform action

        return $response;
    }
}

Registrando un middleware

Middleware globales

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

Asignando un middleware a las rutas

Si deseas asignar un middleware a rutas específicas, deberías primero asignarle una clave al middleware en tu archivo app/Http/Kernel.php. Por defecto, la propiedad$routeMiddleware de esta clase contiene entradas para los middleware incluidos con Laravel. Para agregar uno propio, adjúntalo a esta lista y asígnale una clave de tu elección:

// Within App\Http\Kernel Class...

protected $routeMiddleware = [
    '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 el middleware ha sido definido en el núcleo HTTP, puedes usar el método middleware para asignar un middleware a una ruta:

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

Puedes además asignar múltiples middleware a la ruta:

Route::get('/', function () {
    //
})->middleware('first', 'second');

Cuando asignas middleware, puedes además pasar un nombre de clase plenamente calificado:

use App\Http\Middleware\CheckAge;

Route::get('admin/profile', function () {
    //
})->middleware(CheckAge::class);

Grupos de middleware

Algunas veces puedes querer agrupar varios middleware bajo una sola clave para hacerlos más fáciles de asignar a las rutas. Puedes hacer esto usando la propiedad $middlewareGroups de tu kernel HTTP.

Por defecto, Laravel viene con los grupos de middleware web y api que contienen middleware comunes que puedes aplicar a las rutas de la UI de tu aplicación web y a las rutas de tu API:

/**
 * The application's route middleware groups.
 *
 * @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' => [
        'throttle:60,1',
        'auth:api',
    ],
];

Los grupos de middleware pueden ser asignados a las rutas y las acciones de los controladores usando la misma sintaxis como los middleware individuales. De nuevo, los grupos de middleware hacen más conveniente asignar muchos middleware a una ruta a la vez:

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

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

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

Por defecto, el grupo de middleware web es automáticamente aplicado a tu archivo routes/web.php por el RouteServiceProvider.

Clasificación de middleware

Raramente, necesitarás que tu middleware se ejecute en un orden específico pero no tienes control sobre su orden cuando son asignados a una ruta. En este caso, puedes especificar la prioridad de tu middleware usando la propiedad $middlewarePriority de tu archivo app/Http/Kernel.php:

/**
 * The priority-sorted list of middleware.
 *
 * This forces non-global middleware to always be in the given order.
 *
 * @var array
 */
protected $middlewarePriority = [
    \Illuminate\Session\Middleware\StartSession::class,
    \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    \App\Http\Middleware\Authenticate::class,
    \Illuminate\Session\Middleware\AuthenticateSession::class,
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
    \Illuminate\Auth\Middleware\Authorize::class,
];

Parámetros en los middleware

Los middleware pueden además recibir parámetros adicionales. Por ejemplo, si tu aplicación necesita verificar que el usuario autenticado tiene un «rol» dado antes de ejecutar una acción dada, podrías crear un middleware CheckRole que reciba un nombre de rol como un argumento adicional.

Los parámetros adicionales en el middleware serán pasados al middleware después del argumento $next:

<?php

namespace App\Http\Middleware;

use Closure;

class CheckRole
{
    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string  $role
     * @return mixed
     */
    public function handle($request, Closure $next, $role)
    {
        if (! $request->user()->hasRole($role)) {
            // Redirect...
        }

        return $next($request);
    }

}

Los parámetros en los middleware pueden ser especificados al definir la ruta separando el nombre del middleware y los parámetros con :. Múltiples parámetros deben ser delimitados por comas:

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

Middleware terminable

Algunas veces un middleware puede necesitar hacer algún trabajo después de que la respuesta HTTP ha sido enviada al navegador. Si defines un método terminate en tu middleware y tu servidor web está usando 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;

class StartSession
{
    public function handle($request, Closure $next)
    {
        return $next($request);
    }

    public function terminate($request, $response)
    {
        // Store the session data...
    }
}

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

Al momento de llamar al método terminate de tu middleware, Laravel resolverá una nueva instancia del middleware desde el contenedor de servicios. Si deseas utilizar la misma instancia de middleware cuando los métodos handle y terminate sean llamados, registra el middleware con el contenedor usando el método singleton del contenedor. Típicamente esto debería ser realizado en el método register de tu AppServiceProvider.php:

use App\Http\Middleware\TerminableMiddleware;

/**
 * Register any application services.
 *
 * @return void
 */
public function register()
{
    $this->app->singleton(TerminableMiddleware::class);
}

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Rutas - Documentación de Laravel 6 Lección siguiente Protección CSRF - Documentación de Laravel 6