Middleware

Com funcionen els middleware a Laravel: middleware global, de grups i personalitzats.

Què és un middleware?#

Un middleware és una capa de codi que s'executa entre la recepció d'una petició HTTP i l'execució del controlador. Pensa en els middleware com a filtres: cada petició ha de passar per ells abans d'arribar al seu destí, i cada middleware pot inspeccionar la petició, modificar-la, bloquejar-la o deixar-la continuar.

Un exemple pràctic: el middleware auth de Laravel comprova si l'usuari està autenticat. Si ho està, la petició continua cap al controlador. Si no, redirigeix l'usuari a la pàgina de login.

Crear un middleware#

Per crear un middleware personalitzat, utilitza la comanda Artisan:

php artisan make:middleware CheckAge

Això genera un fitxer a app/Http/Middleware/CheckAge.php amb un mètode handle(). Aquest mètode rep la petició i un closure $next que representa el següent pas de la cadena:

namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
 
class CheckAge
{
    public function handle(Request $request, Closure $next)
    {
        if ($request->age < 18) {
            return redirect('home');
        }
 
        return $next($request);
    }
}

Cridar $next($request) passa la petició al següent middleware o al controlador. Si no el crides, la petició queda bloquejada a aquest punt. Això és el que passa quan fas un redirect() o un abort(): la petició no continua.

Registrar middleware#

A Laravel 11, els middleware es registren al fitxer bootstrap/app.php. Pots registrar-los de manera global (s'executen a totes les peticions) o com un alias que després assignes a rutes concretes:

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    // Middleware global: s'executa a totes les peticions
    $middleware->append(CheckAge::class);
 
    // Alias: per usar a rutes específiques
    $middleware->alias([
        'check.age' => CheckAge::class,
    ]);
})

Els middleware globals són adequats per a funcionalitats que afecten tota l'aplicació, com el manteniment de sessions o la protecció CSRF. Els alias et permeten aplicar middleware de manera selectiva.

Assignar middleware a rutes#

Un cop registrat amb un alias, pots assignar un middleware a una ruta concreta o a un grup de rutes:

// A una ruta individual
Route::get('/adult', function () {
    // Només accessible si passa el middleware
})->middleware('check.age');
 
// Múltiples middleware
Route::get('/admin', function () {
    // ...
})->middleware(['auth', 'admin']);

Per aplicar el mateix middleware a diverses rutes, pots fer servir un grup:

Route::middleware(['auth'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::get('/perfil', [ProfileController::class, 'show']);
    Route::get('/configuracio', [SettingsController::class, 'index']);
});

Totes les rutes dins del grup compartiran el middleware auth, de manera que qualsevol usuari no autenticat serà redirigit al login.

Middleware amb paràmetres#

Els middleware també poden rebre paràmetres addicionals. Això és útil per crear middleware reutilitzables que es comporten diferent segons el context:

class CheckRole
{
    public function handle(Request $request, Closure $next, string $role)
    {
        if ($request->user()->role !== $role) {
            abort(403);
        }
 
        return $next($request);
    }
}

A les rutes, passes el paràmetre separat per dos punts:

Route::get('/admin', function () {
    // Només per a administradors
})->middleware('role:admin');
 
Route::get('/editor', function () {
    // Només per a editors
})->middleware('role:editor');

El valor admin o editor arriba al tercer paràmetre del mètode handle(). Així, un sol middleware serveix per controlar l'accés a múltiples rols.

Middleware terminables#

Alguns middleware necessiten fer feina després que la resposta s'ha enviat al navegador. Per exemple, desar logs o alliberar recursos. Per a això, pots definir un mètode terminate():

class LogResponse
{
    public function handle(Request $request, Closure $next)
    {
        return $next($request);
    }
 
    public function terminate(Request $request, $response)
    {
        // S'executa després d'enviar la resposta
        Log::info('Petició processada', [
            'url' => $request->url(),
            'status' => $response->status(),
        ]);
    }
}

El mètode terminate() s'executa un cop la resposta ja ha estat enviada al client, de manera que no afecta el temps de càrrega de la pàgina.