
I middleware forniscono un comodo meccanismo per gestire le richieste HTTP che arrivano alla nostra applicazione. Ad esempio, Laravel include un middleware che verifica se l'utente è autenticato e in caso negativo, lo reindirizza alla schermata di login.
Lo stack middleware è costruito intorno al componente Illuminate\Contracts\Http\Kernel di Laravel che fornisce un potente sistema di amministrazione delle richieste HTTP. In realtà, tutti i middleware integrati in Laravel sono un wrapper per questo componente.
Questo contratto richiede due metodi: handle() e terminate(). Il metodo handle() riceve come argomenti le istanze di Request e Closure. Di solito, si utilizza l'istanza Request per ispezionare la richiesta HTTP che arriva dall’applicazione. L’istanza Closure, invece, definisce cosa deve accadere successivamente a quella richiesta.
Il metodo terminate() viene richiamato per performare un’azione in background, dopo che la richiesta è stata elaborata.
Creare un middleware personalizzato
Per aggiungere un nuovo middleware alla nostra applicazione possiamo usare artisan con la solita sintassi per generare un nuovo componente
php artisan make:middleware CarMiddleware
Il nostro nuovo middleware sarà collocato all’interno della cartella app/Http/Middleware. Apriamolo e implementiamo nel metodo handle() un controllo casuale, giusto per fare qualche test.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class CarMiddleware
{
/**
* @param Request $request
* @param Closure $next
* @return mixed|void
* @throws \Exception
*/
public function handle(Request $request, Closure $next)
{
if (random_int(0, 1)) {
return $next($request);
}
abort(403);
}
}
Il codice è abbastanza intuitivo: generiamo un booleano casuale, se è vero andiamo avanti con la richiesta, altrimenti fermiamo l’applicazione con un errore HTTP Error 403 Forbidden.
Registriamo il middleware! Apriamo il file app/Http/Middleware/Kernel.php e individuiamo il blocco $routeMiddleware. Qui inseriamo una nuova coppia chiave/valore per associare ad un alias la classe nel nostro nuovo middleware:
protected $routeMiddleware = [
…
// Il nostro nuovo middleware
'car_guard' => \App\Http\Middleware\CarMiddleware::class,
];
Ripuliamo la cache dell’applicativo lanciando questo comando
php artisan optimize
Il nostro middleware può essere associato a diversi componenti di Laravel, come le rotte e i controllers. Personalmente preferisco associarlo alle rotte, ma per completezza vedremo entrambe le strade.
Associare un middleware alle rotte
In questo primo approccio applicheremo il middleware al gruppo delle rotte per la crud dell’entità Car. Quindi apriamo nuovamente il file routes/web.php e in qualsiasi punto della concatenazione dei metodi della Facade Route, aggiungiamo il metodo middleware(). Questo accetta come parametro sia un’array che una stringa. Il parametro che dobbiamo passare è l’alias del middleware CarMiddleware che abbiamo definito nel Kernel.
Route::controller(\App\Http\Controllers\CarController::class)
->prefix('car')->as('car.')->middleware('car_guard')
->group(callback: function (
) {
Route::get('/', 'index')->name('index');
Route::get('/create', 'create')->name('create');
Route::get('/show/{car}', 'show')->name('show');
Route::post('/store', 'store')->name('store');
Route::get('/edit/{car}', 'edit')->name('edit');
Route::put('/update{car}', 'update')->name('update');
Route::match(['get', 'post'], '/destroy/{car}', 'destroy')->name('destroy');
});
Dopo aver lanciato nuovamente il comando artisan per ottimizzare l’applicazione, vedremo che invocando una qualsiasi rotta del gruppo car, entrerà in azione il middleware, negandoci o meno l’accesso, a seconda della casualità che abbiamo impostato nel metodo handle() del middleware stesso.
Ora eliminiamo l’associazione del middleware al gruppo delle rotte per procedere con la seconda opzione.
Associare un middleware al Controller
Per ottenere il risultato precedente, agendo sul Controller, dobbiamo implementare il costruttore della classe CarController e invocare al suo interno il metodo middleware, passando come parametro il solito alias.
public function __construct()
{
$this->middleware('car_guard');
}
Il risultato sarà il medesimo dell’esempio precedente. Però qui abbiamo la possibilità di escludere determinate rotte dall’azione del middleware, in maniera molto rapida. Se ad esempio vogliamo che l’accesso al rotta /create sia esente dall’azione del middleware, possiamo usare il metodo except() della classe ControllerMiddlewareOptions che è il valore di ritorno del metodo che abbiamo appena visto.
public function __construct()
{
$this->middleware('car_guard')->except('create');
}
Il risultato si può ottenere anche con il primo approccio, ma avremmo dovuto associare il metodo middleware a tutte le rotte della crud, ad eccezione della rotta create:
Route::controller(\App\Http\Controllers\CarController::class)
->prefix('car')->as('car.')
->group(callback: function (
) {
Route::get('/', 'index')->name('index')->middleware('car_guard');
Route::get('/create', 'create')->name('create');
Route::get('/show/{car}', 'show')->name('show')->middleware('car_guard');
Route::post('/store', 'store')->name('store')->middleware('car_guard');
Route::get('/edit/{car}', 'edit')->name('edit')->middleware('car_guard');
Route::put('/update{car}', 'update')->name('update')->middleware('car_guard');
Route::match(['get', 'post'], '/destroy/{car}', 'destroy')->name('destroy')->middleware('car_guard');
});
Per un programmatore che conosce tutte le scorciatoie da tastiera del suo IDE di sviluppo non ci sono problemi, ma per chi è meno scaltro è indubbiamente più rapido agire direttamente sul controller.
Adesso continuiamo alla lezione successiva, ma prima di andare avanti eliminiamo ogni riferimento a questo middleware, che non ha più nessuna ragione di esistere.