Il routing è uno degli aspetti essenziali di una applicazione web ed è, ovviamente, alla base di un progetto Laravel. Con routing si intende il meccanismo per il quale le richieste HTTP vengono instradate (route) alla porzione di codice che le gestirà ed è alla base del ciclo di vita di una richiesta HTTP gestita da un applicativo Laravel.
In Laravel il routing viene configurato e gestito registrando delle rotte nei file routes/web.php e routes/api.php, che rispettivamente gestiscono le richieste relative alla interfaccia web dell’applicazione e le richieste di tipo API RESTful.
Conoscere i meccanismi di funzionamento del routing in Laravel è importante per comprendere meglio la finalità, il comportamento previsto e le interazioni tra i vari componenti che costituiscono una web app Laraver, riuscendo, quindi, a superare l’iniziale senso di “succede come per magia”.
Vediamo, quindi, nel dettaglio il ciclo di vita di una richiesta HTTP in una applicazione Laravel, ovvero cosa accade “sul server” quando arriva una richiesta HTTP di un client.
Server web e applicativo web Laravel
Nel mondo del web è importante distinguere tra server web e applicativo web.
Il server web è un applicativo a sé stante che nella pratica si occupa di ricevere le richieste HTTP dei client e decidere quale file è associato alla risorsa richiesta.
Facciamo un breve esempio: abbiamo una directory di nome /opt/website/ con dentro i file index.html e style.css. Se avviamo il server web configurato nel modo seguente:
root: /opt/website/
req: / -> index.html
req: /* -> *
Stiamo in pratica dicendo che:
- se il client manda una richiesta HTTP per la risorsa /, allora il server restituisce il file /opt/website/index.html
- per ogni altra richiesta il server restituisce il file richiesto se presente in /opt/website/website/
Se il file richiesto non esiste, il web server fornirà una risposta HTTP con status code 404 Not Found.
Un server web opera a livello richieste e risposte HTTP, limitandosi a smistare le richieste verso i file inclusi in una certa directory.
Un sito web statico, fatto cioè da sole pagine HTML che non cambiano in base alla richiesta effettuata o che non richiedono la presenza di form per l’invio dei dati, può essere reso disponibile sul web caricando i file su un macchina in cui gira solo un server web, gestendo in modo opportuno link e directory su disco.
Un’ applicazione Laravel necessita di un server web per ricevere richieste e restituire risposte HTTP, ma il server web viene praticamente configurato nel seguente modo:
root: /opt/app-laravel/public/
req: /* -> run(php index.php)
Ovvero: a ogni richiesta HTTP ricevuta esegui lo script public/index.php del progetto Laravel.
Il file index.php in Laravel
Il file public/index.php di un progetto Laravel è, quindi, lo script PHP che viene invocato ogni volta che arriva una richiesta al nostro server.
In questo file non è presente molto codice, ma solo quanto serve per caricare il resto del framework Laravel. Al suo interno infatti, a parte il necessario require dell’autoload, troviamo solo
$app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle($request = Request::capture())->send(); $kernel->terminate($request, $response);
che possiamo interpretare nel seguente modo:
- recupera una istanza dell’applicazione Laravel $app
- carica il $kernel opportuno (in base al tipo di richiesta arrivata sarà un kernel di tipo HTTP o console)
- fai gestire la $request arrivata al kernel e invia una $response
Questo mini ciclo è ripetuto ad ogni richiesta che arriva al server, passando la richiesta da uno strato all’altro fino a giungere all’effettivo codice che gestirà e restituirà il contenuto della risposta.
La selezione del middleware
Le richieste di tipo HTTP proseguono il viaggio seguendo una via apparentemente tortuosa, legata nuovamente al tipo di richiesta arrivata.
Le versioni più recenti di Laravel (in questa guida stiamo usando la versione 9.x attualmente disponibile) offrono, infatti, la possibilità di distinguere due diversi “gruppi” di rotte, definiti middleware.
Quando viene inizializzata l’applicazione Laravel, viene caricato un servizio interno per la gestione delle rotte. Tale servizio corrisponde al contenuto del file app/Providers/RouteServiceProvider.php, ovvero la classe App\Providers\RouteServiceProvider. La versione di default di questo servizio prevede quanto segue:
$this->routes(function () { Route::middleware('api') ->prefix('api') ->group(base_path('routes/api.php')) Route::middleware('web') ->group(base_path('routes/web.php')); });
ovvero, tutte le richieste di risorse il cui path inizia per api appartengono al middleware api e verranno smistate al file routes/api.php per un successivo ulteriore smistamento. Ogni altra richiesta è smistata verso il middleware web e verrà ulteriormente smistata dal file routes/web.php.
Richiesta | Middleware |
---|---|
GET /api/orders/ |
api |
POST /page/form |
web |
GET /docs/api/ |
web |
GET /blog/2022/12/23/post.html |
web |
DELETE /api/orders/23455 |
api |
DELETE /order/23455 |
web |
NOTA: quanto descritto finora è la configurazione presente in un’ applicazione Laravel appena creata dal template di default. È possibile cambiare queste configurazioni in base alle proprie esigenze, per esempio rimuovere completamente il middleware api nel caso in cui non siano necessarie delle API RESTful, ma si voglia usare Laravel per realizzare solo in sito web.
La selezione della rotta registrata
Una volta individuato il middleware, la nostra richiesta HTTP prosegue il suo viaggio nella sua destinazione finale.
La responsabilità è ora data ai file routes/web.php e routes/api.php che, sebbene in modi lievemente diversi data la diversa natura del tipo di richiesta che stanno gestendo, contengono al loro interno le effettive rotte registrate.
// routes/web.php Route::get('/', function () { return view('welcome'); }); Route::get('/hello', function () { return 'Hello Laravel'; }); // routes/api.php Route::middleware('auth:sanctum')->get('/orders', function (Request $request) { return $request->orders(); });
Se esiste, quindi, una rotta registrata per il path della richiesta che si sta gestendo, allora sarà eseguita la corrispondente funzione e verrà rimandata indietro quanto restituito dalla funzione stessa.
Ad esempio, limitandoci alle sole richieste nel middleware web:
- nel caso in cui arrivasse una richiesta GET /hello, allora verrà rimandato indietro il testo “Hello Laravel” che diventerà il body della risposta HTTP con status 200 OK
- per la richiesta GET / verrà invocata una funzione dedicata che fa il rendering di una view, ovvero cerca il file template associato, lo elabora e restituisce il risultato come testo (molto probabilmente HTML) che, anche in questo, diventerà il body di una risposta HTTP con status 200 OK
- ogni altra richiesta, sia per metodo che per risorsa richiesta, non avendo una corrispettiva rotta registrata, non potrà essere soddisfatta e Laravel si occuperà di preparare e restituire una risposta con status 404 Not Found
Il file routes/web.php è, quindi, in pratica, il file da consultare per conoscere quali sono le rotte attualmente registrate e il punto di partenza quando si deve andare a definire una nuova rotta.
NOTA: Oltre a quanto descritto finora, Laravel permette anche un’altra modalità per definire una rotta, tramite controller; Per ora basti pensarlo come un modo per raggruppare in una singola classe rotte che fanno parte dello stesso aggregato.
Artisan e le rotte in Laravel
Per ottenere un elenco completo delle rotte registrate dall’applicazione Laravel, è anche possibile usare l’opportuno comando Artisan:
php artisan route:list
L’opzione -v mostra anche i middleware associati, mentre l’opzione –path permette di mostrare solo le rotte che contengono una certa stringa nell’URI:
$ php artisan route:list --path=user -v GET|HEAD api/user ⇂ api ⇂ App\Http\Middleware\Authenticate:sanctum GET|HEAD user ⇂ web
Se avete aggiunto una rotta e non compare eseguendo questo comando, probabilmente non è stata aggiunta nel modo corretto. Laravel non sarà, quindi, in grado di gestire richieste per quella rotta.
Conclusioni
- il routing è il modo con cui una richiesta HTTP di una certa URI viene fatta arrivare a un file o a una porzione di codice che si occupa di creare il contenuto della risposta
- in Laravel è, praticamente, sufficiente registrare una rotta per gestire le richieste
- una rotta registrata è individuata dal metodo HTTP e dal path della risorsa
- una rotta registrata indica anche cosa fare per costruire la risposta da mandare al cliente
- tutte le rotte registrate sono presenti nel file routes/web.php
- Laravel nasconde allo sviluppatore web gran parte della gestione di basso livello dei meccanismi di routing, ma è sempre possibile customizzare la propria app andando a modificare il contenuto di specifici file e classi