# Introduction

El middleware proporciona un mecanismo conveniente para filtrar las solicitudes HTTP que entran en 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. Sin embargo, si el usuario está autenticado, el middleware permitirá que la solicitud siga adelante en la aplicación.

Se puede escribir un middleware adicional para realizar una variedad de tareas además de la autenticación. Un middleware CORS podría ser responsable de agregar los encabezados adecuados a todas las respuestas que salen de su aplicación. Un middleware de registro podría registrar todas las solicitudes entrantes a su aplicación.

Hay varios middleware incluidos en el marco de trabajo de Laravel, incluyendo el middleware para la autenticación y la 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 CheckAge

Este comando creará un nuevo middleware llamado CheckAge en el directorio app/Http/Middleware. Este middleware solo permitirá acceso a la ruta si la age suministrada es mayor a 200. De otra forma, el middleware redirigirá a los usuarios de vuelta a la URI 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 puede verse, si la age es menor o igual a 200, el middleware retornará un redirect HTTP al cliente; de otra manera la request será ejecutada por la aplicación. Para pasar la petición hacia abajo en la aplicación (permitir al middleware "pasar"), simplemente hay que llamar al callback $next con $request.

Es mejor imaginar el middleware como una serie de "capas" que las peticiones HTTP deben pasar antes de que lleguen a tu 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 puedes teclear cualquier dependencia que necesites dentro del constructor de un middleware.

Antes y después del Middleware

Que un middleware se ejecute antes o después de una solicitud depende del propio middleware. 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;

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

        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;

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

        // Perform action

        return $response;
    }
}

# Registro Middleware

Middleware Global

Si se desea que un middleware se ejecute en todas las peticiones HTTP de la aplicación, simplemente debe listar el middleware en la propiedad $middleware de la clase app/Http/Kernel.php.

Asignar Middleware a Rutas

Si desea asignar un middleware a rutas específicas, primero debe asignar al middleware una clave en su archivo app/Http/Kernel.php. Por defecto, la propiedad $routeMiddleware de esta clase contiene entradas para el middleware incluido en Laravel. Para añadir el tuyo, añádelo 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 que el middleware ha sido registrado en el kernel HTTP, se puede utilizar el identificador de middleware para asignarlo a una ruta:

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

También puede asignar varios middleware a la ruta:

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

Al asignar un middleware, se puede también pasar el nombre completo de la clase:

use App\Http\Middleware\CheckAge;

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

Al asignar el middleware a un grupo de rutas, es posible que en ocasiones tenga que 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\CheckAge;

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

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

El método withoutMiddleware sólo puede eliminar el middleware de ruta y no se aplica global middleware.

Grupos de Middleware

En ocasiones es útil agrupar varios middleware sobre un mismo identificador haciendo la asignación a rutas mucho más simple. Esto se puede hacer utilizando la propiedad $middlewareGroups del kernel HTTP.

Por defecto, Laravel incluye los grupos de middleware web y api que contienen el middleware común que se suele aplicar a las rutas de web UI y 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 asignarse a las rutas y acciones de los controladores utilizando la misma sintaxis que el middleware individual. 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 () {
    //
});

El grupo web se aplica directamente al archivo routes/web.php a través del RouteServiceProvider.

Clasificación del Middleware

En raras ocasiones, puede necesitar su middleware para ejecutar 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 su middleware utilizando la propiedad $middlewarePriority de su 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,
    \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
    \Illuminate\Routing\Middleware\ThrottleRequests::class,
    \Illuminate\Session\Middleware\AuthenticateSession::class,
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
    \Illuminate\Auth\Middleware\Authorize::class,
];

# Middleware con Parametros

Los middleware pueden recibir parámetros adicionales. Por ejemplo, si la aplicación necesita verificar que el usuario autenticado tiene asignado cierto "rol" antes de ejecutar una acción, puede crearse un middleware CheckRole que reciba el nombre del rol como parámetro adicional.

Los parámetros adicionales del middleware deben ser pasados después de 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 del middleware pueden ser especificados cuando se define la ruta separando el nombre del middleware y los parámetros con un :. Los parámetros múltiples deben estar delimitados por comas:

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

# Middleware Terminable

A veces un middleware puede necesitar hacer algún trabajo después de que la respuesta HTTP ha sido enviada al navegador. Si define un método de "terminate" en su middleware y su servidor web está usando FastCGI, el método de "terminate" en su middleware y su servidor web está usando FastCGI, el método de "terminación" 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...
    }
}

The terminate method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of route or global middleware in the app/Http/Kernel.php file.

Cuando se llama al método terminate en el middleware, Laravel resolverá una nueva instancia del middleware desde el service container. Si se desea usar la misma instancia cuando los métodos handle y terminate son llamados, debe registrarse en el container el middleware usando el método singleton del container.

use App\Http\Middleware\TerminableMiddleware;

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