Middleware

Introduzione

I Middleware offrono un meccanismo comodo per analizzare e filtrare le richieste HTTP che entrano nella tua applicazione. Ad esempio, Laravel include un middleware che verifica se l’utente della tua applicazione è autenticato. Se l’utente non è autenticato, il middleware reindirizzerà l’utente alla schermata di accesso della tua applicazione. Tuttavia, se l’utente è autenticato, il middleware permette alla richiesta di procedere ulteriormente nell’applicazione.

È possibile scrivere middleware aggiuntivi per eseguire diverse attività oltre all’autenticazione. Per esempio, un middleware per il logging potrebbe registrare tutte le richieste in arrivo nella tua applicazione. Laravel include diversi middleware, tra cui quelli per l’autenticazione e la protezione da CSRF. Tutti i middleware definiti dall’utente si trovano tipicamente nella directory app/Http/Middleware della tua applicazione.

Definizione dei Middleware

Per creare un nuovo middleware, utilizza il comando Artisan make:middleware:

php artisan make:middleware EnsureTokenIsValid

Questo comando inserirà una nuova classe EnsureTokenIsValid nella directory app/Http/Middleware. In questo middleware, consentiremo l’accesso alla route solo se il parametro token fornito corrisponde a un valore specificato. Altrimenti, reindirizzeremo gli utenti all’URI /home:

    <?php

    namespace App\Http\Middleware;

    use Closure;
    use Illuminate\Http\Request;
    use Symfony\Component\HttpFoundation\Response;

    class EnsureTokenIsValid
    {
        /**
         * Handle an incoming request.
         *
         * @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);
        }
    }

Come puoi vedere, se il token fornito non corrisponde al nostro token segreto, il middleware effettuerà un reindirizzamento; altrimenti, la richiesta verrà inoltrata all’applicazione. Per inoltrare la richiesta più in profondità nell’applicazione (permettere al middleware di "passare"), viene chiamata la callback $next con la $request.

È meglio immaginare i middleware come una serie di "livelli" attraverso i quali le richieste HTTP devono passare prima di raggiungere la tua applicazione. Ogni livello può esaminare la richiesta e persino rifiutarla completamente.

Tutti i middleware sono risolti tramite il container dei servizi, quindi puoi specificare i tipi di qualsiasi dipendenza di cui hai bisogno nel costruttore di un middleware.

Middleware e Risposte

Naturalmente, un middleware può eseguire delle operazioni prima o dopo aver inoltrato la richiesta più in profondità nell’applicazione. Ad esempio, il middleware seguente eseguirà un’operazione prima che la richiesta venga gestita dall’applicazione:

<?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
    {
        // Perform action

        return $next($request);
    }
}

Tuttavia, questo altro middleware di seguito eseguirà la sua operazione dopo che la richiesta è stata gestita dall’applicazione:

<?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);

        // Perform action

        return $response;
    }
}

Registrare Middleware

Middleware Globale

Se vuoi che un middleware venga eseguito durante ogni richiesta HTTP alla tua applicazione, puoi aggiungerlo allo stack dei middleware globali nel file bootstrap/app.php della tua applicazione:

use App\Http\Middleware\EnsureTokenIsValid;

->withMiddleware(function (Middleware $middleware) {
     $middleware->append(EnsureTokenIsValid::class);
})

L’oggetto $middleware fornito alla closure withMiddleware è un’istanza di Illuminate\Foundation\Configuration\Middleware ed è responsabile della gestione dei middleware assegnati alle route della tua applicazione. Il metodo append aggiunge il middleware alla fine della lista dei middleware globali. Se vuoi aggiungere un middleware all’inizio della lista, dovresti usare il metodo prepend.

Gestione manuale del middleware globale predefinito di Laravel

Se desideri gestire manualmente la pila di middleware globale di Laravel, puoi fornire la pila predefinita dei middleware globali di Laravel al metodo use. Successivamente, puoi modificare la pila di middleware predefinita secondo necessità:

->withMiddleware(function (Middleware $middleware) {
    $middleware->use([
        \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class,
        // \Illuminate\Http\Middleware\TrustHosts::class,
        \Illuminate\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Http\Middleware\ValidatePostSize::class,
        \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ]);
})

Assegnare Middleware alle Rotte

Se desideri assegnare middleware a rotte specifiche, puoi usare il metodo middleware durante la definizione della rotta:

use App\Http\Middleware\EnsureTokenIsValid;

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

Puoi assegnare più middleware alla rotta passando un array di nomi di middleware al metodo middleware:

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

Escludere un Middleware

Quando assegni un middleware a un gruppo di rotte, potresti aver bisogno di evitare che il middleware venga applicato a una singola rotta all’interno del gruppo. Puoi fare questo usando il metodo withoutMiddleware:

use App\Http\Middleware\EnsureTokenIsValid;

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

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

Puoi anche escludere un determinato set di middleware da un intero gruppo di definizioni di rotte:

use App\Http\Middleware\EnsureTokenIsValid;

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

Il metodo withoutMiddleware può rimuovere solo middleware di rotta e non si applica ai middleware globali.

Gruppi Middleware

A volte potresti voler raggruppare diversi middleware sotto una singola chiave per assegnarli più facilmente alle rotte. Puoi fare questo utilizzando il metodo appendToGroup nel file bootstrap/app.php della tua applicazione:

use App\Http\Middleware\First;
use App\Http\Middleware\Second;

->withMiddleware(function (Middleware $middleware) {
    $middleware->appendToGroup('group-name', [
        First::class,
        Second::class,
    ]);

    $middleware->prependToGroup('group-name', [
        First::class,
        Second::class,
    ]);
})

I gruppi middleware possono essere assegnati alle rotte e alle azioni dei controller usando la stessa sintassi dei singoli middleware:

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

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

I Gruppi Middleware Predefiniti di Laravel

Laravel include i gruppi middleware predefiniti web e api che contengono middleware comuni che potresti voler applicare alle tue route web e API. Ricorda, Laravel applica automaticamente questi gruppi middleware ai corrispondenti file routes/web.php e routes/api.php:

Il Gruppo Middleware web
Illuminate\Cookie\Middleware\EncryptCookies
Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse
Illuminate\Session\Middleware\StartSession
Illuminate\View\Middleware\ShareErrorsFromSession
Illuminate\Foundation\Http\Middleware\ValidateCsrfToken
Illuminate\Routing\Middleware\SubstituteBindings
Il Gruppo Middleware api
Illuminate\Routing\Middleware\SubstituteBindings

Se desideri aggiungere o anteporre middleware a questi gruppi, puoi utilizzare i metodi web e api all’interno del file bootstrap/app.php della tua applicazione. I metodi web e api sono alternative comode al metodo appendToGroup:

    use App\Http\Middleware\EnsureTokenIsValid;
    use App\Http\Middleware\EnsureUserIsSubscribed;

    ->withMiddleware(function (Middleware $middleware) {
        $middleware->web(append: [
            EnsureUserIsSubscribed::class,
        ]);

        $middleware->api(prepend: [
            EnsureTokenIsValid::class,
        ]);
    })

Puoi persino sostituire una delle voci predefinite del gruppo middleware di Laravel con un middleware personalizzato:

    use App\Http\Middleware\StartCustomSession;
    use Illuminate\Session\Middleware\StartSession;

    $middleware->web(replace: [
        StartSession::class => StartCustomSession::class,
    ]);

Oppure, puoi rimuovere completamente un middleware:

    $middleware->web(remove: [
        StartSession::class,
    ]);

Gestire Manualmente i Gruppi Middleware Predefiniti di Laravel

Se desideri gestire manualmente tutti i middleware all’interno dei gruppi middleware predefiniti web e api di Laravel, puoi ridefinire completamente i gruppi. L’esempio seguente definirà i gruppi middleware web e api con i loro middleware predefiniti, consentendoti di personalizzarli secondo necessità:

    ->withMiddleware(function (Middleware $middleware) {
        $middleware->group('web', [
            \Illuminate\Cookie\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
        ]);

        $middleware->group('api', [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            // 'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ]);
    })

> Di default, i gruppi middleware `web` e `api` vengono applicati automaticamente ai file corrispondenti `routes/web.php` e `routes/api.php` della tua applicazione dal file `bootstrap/app.php`.

<a name="alias-dei-middleware"></a>
### Alias dei Middleware

Puoi assegnare alias ai middleware nel file `bootstrap/app.php` della tua applicazione. Gli alias dei middleware ti permettono di definire un alias breve per una determinata classe di middleware, il che può essere particolarmente utile per middleware con nomi di classi lunghi:

```php
    use App\Http\Middleware\EnsureUserIsSubscribed;
    
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'subscribed' => EnsureUserIsSubscribed::class
        ]);
    })

Una volta che l’alias del middleware è stato definito nel file bootstrap/app.php della tua applicazione, puoi utilizzare l’alias durante l’assegnazione del middleware alle route:

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

Per comodità, alcuni middleware integrati di Laravel hanno già alias predefiniti. Ad esempio, il middleware auth è un alias per il middleware Illuminate\Auth\Middleware\Authenticate. Di seguito è riportato un elenco degli alias dei middleware predefiniti:

Alias Middleware
auth Illuminate\Auth\Middleware\Authenticate
auth.basic Illuminate\Auth\Middleware\AuthenticateWithBasicAuth
auth.session Illuminate\Session\Middleware\AuthenticateSession
cache.headers Illuminate\Http\Middleware\SetCacheHeaders
can Illuminate\Auth\Middleware\Authorize
guest Illuminate\Auth\Middleware\RedirectIfAuthenticated
password.confirm Illuminate\Auth\Middleware\RequirePassword
precognitive Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests
signed Illuminate\Routing\Middleware\ValidateSignature
subscribed \Spark\Http\Middleware\VerifyBillableIsSubscribed
throttle Illuminate\Routing\Middleware\ThrottleRequests or Illuminate\Routing\Middleware\ThrottleRequestsWithRedis
verified Illuminate\Auth\Middleware\EnsureEmailIsVerified

Ordinamento dei Middleware

Raramente, potresti avere bisogno che il tuo middleware venga eseguito in un ordine specifico senza poter controllare l’ordine in cui sono assegnati alla route. In queste situazioni, puoi specificare la priorità del middleware utilizzando il metodo priority nel file bootstrap/app.php della tua applicazione:

->withMiddleware(function (Middleware $middleware) {
    $middleware->priority([
        \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\Foundation\Http\Middleware\ValidateCsrfToken::class,
        \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        \Illuminate\Routing\Middleware\ThrottleRequests::class,
        \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ]);
})

Parametri del Middleware

Il middleware può anche ricevere parametri aggiuntivi. Ad esempio, se la tua applicazione deve verificare che l’utente autenticato abbia un determinato "role" prima di eseguire una specifica azione, potresti creare un middleware EnsureUserHasRole che riceve il nome di un role come argomento aggiuntivo.

I parametri aggiuntivi del middleware verranno passati al middleware dopo l’argomento $next:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserHasRole
{
    /**
     * Gestisci una richiesta in arrivo.
     *
     * @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)) {
            // Reindirizza...
        }

        return $next($request);
    }

}

I parametri del middleware possono essere specificati quando si definisce la rotta separando il nome del middleware e i parametri con un ::

use App\Http\Middleware\EnsureUserHasRole;

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

Parametri multipli possono essere delimitati da virgole:

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

Middleware Terminabili

A volte un middleware potrebbe aver bisogno di eseguire alcune operazioni dopo che la risposta HTTP è stata inviata al browser. Se definisci un metodo terminate nel tuo middleware e il tuo server web utilizza FastCGI, il metodo terminate verrà chiamato automaticamente dopo che la risposta è stata inviata al browser:

<?php

namespace Illuminate\Session\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class TerminatingMiddleware
{
    /**
     * Gestisce una richiesta in arrivo.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    /**
     * Gestisce le operazioni dopo che la risposta è stata inviata al browser.
     */
    public function terminate(Request $request, Response $response): void
    {
        // ...
    }
}

Il metodo terminate dovrebbe ricevere sia la richiesta che la risposta. Una volta definito un middleware terminabile, dovresti aggiungerlo alla lista dei middleware delle rotte o globale nel file bootstrap/app.php della tua applicazione.

Quando viene chiamato il metodo terminate sul tuo middleware, Laravel risolverà una nuova istanza del middleware dal service container. Se desideri utilizzare la stessa istanza del middleware quando vengono chiamati i metodi handle e terminate, registra il middleware nel contenitore utilizzando il metodo singleton del contenitore. Tipicamente, questo dovrebbe essere fatto nel metodo register del tuo AppServiceProvider:

use App\Http\Middleware\TerminatingMiddleware;

/**
 * Registra i servizi dell'applicazione.
 */
public function register(): void
{
    $this->app->singleton(TerminatingMiddleware::class);
}
Lascia un commento

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *