CORS, acronimo di Cross-Origin Resource Sharing è un meccanismo che definisce le modalità di comunicazione tra browser e server, per consentire, ad una richiesta HTTP, di atterrare su un dominio diverso da quello di partenza.
la problematica
Alzi la mano chi non si è mai trovato (almeno per una volta) di fronte al messaggio d’errore:
CORS: XMLHttpRequest cannot load https://xxx.com/api Request header field Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers in preflight response.
L’errore in questione è frutto di una misura di sicurezza, implementata dai browser, nota come same-origin policy (letteralmente “politica della stessa origine”). La same-origin policy impedisce agli script (client-side), di interagire con risorse provenienti da un dominio diverso da quello di partenza (per evitare che due applicazioni web si manomettano a vicenda). Potrete trovare una descrizione dettagliata nell’articolo “CORS in dettaglio“.
Vediamo quindi come abilitare CORS sulle nostre applicazioni Laravel, per “bypassare” la same-origin policy.
I Middleware
Chi già conosce Laravel avrà sicuramente avuto a che fare con i middleware, per tutti gli altri segue un breve ripasso.
Potete immaginare i middleware come dei filtri, che ci consentono di intercettare le request e le response della nostra applicazione.
Possiamo generare un middleware con il comando:
php artisan make:middleware TestMiddleware
Che provvederà a creare il file app/Http/Middleware/TestMiddleware.php
namespace App\Http\Middleware; use Closure; class TestMiddleware { public function handle($request, Closure $next) { return $next($request); } }
Il funzionamento del middleware è estremamente semplice: ad ogni richiesta HTTP, da parte dell’utente, verrà invocato il metodo handle.
L’istruzione $next($request)
, all’interno di handle, consente di proseguire con l’elaborazione della richiesta (middleware successivo) e ritorna la response.
Possiamo banalmente manipolare/bloccare la request o la response, andando ad agire (rispettivamente) prima dell’istruzione $next($request)
o sul suo valore di ritorno:
public function handle($request, Closure $next) { //Eseguo un controllo sulla request if($request->api_token !== '...') { //Abort della richiesta corrente abort(403); } $response = $next($request); //Eseguo azioni sulla response $response->header('MyCustomHeader', 'CustomValue'); return $response; }
A questo punto, una volta scritto il nostro middleware, come possiamo far sì che Laravel lo utilizzi per tutte le richieste utente? Ci viene in aiuto il file app/Http/Kernel.php, nel quale possono essere definiti i middleware da assegnare (registrare) globalmente a tutte le richieste:
protected $middleware = [ [...] \App\Http\Middleware\TestMiddleware::class, ];
Un middleware per abilitare CORS
Abbiamo visto cos’è CORS e conosciamo il principio di funzionamento dei middleware: non ci resta che scrivere un middleware, che vada ad aggiungere gli header necessari per gestire correttamente CORS.
Generiamo quindi un middleware apposito:
php artisan make:middleware CorsMiddleware
E modifichiamo il file app/Http/Middleware/CorsMiddleware.php nel seguente modo:
class CorsMiddleware { public function handle($request, Closure $next) { return $next($request) ->header('Access-Control-Allow-Origin', '*') ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS') ->header('Access-Control-Allow-Headers', 'Content-Type, X-Auth-Token, Origin, Authorization'); } }
Il middleware in questione si occupa (banalmente) di aggiungere, sulla nostra response, gli header:
- Access-Control-Allow-Origin, valorizzato con la “lista” di domini che vogliamo abilitare (completi di protocollo) oppure con il wildcard character “*” (che abiliterà l’accesso alla risorsa per ogni dominio),
- Access-Control-Allow-Methods, valorizzato con la “lista” di metodi HTTP che vogliamo abilitare,
- Access-Control-Allow-Headers, valorizzato con la “lista” di header che vogliamo sbloccare (utile sulle preflight request, quando vogliamo abilitare l’inoltro di header diversi da Accept, Accept-Language, Content-Language, Content-Type);
A questo punto non resta che modificare (o parametrizzare) il valore degli header, per adattarlo alle vostre specifiche esigenze.
Registriamo infine il nostro middleware, per rendere disponibili gli header globalmente (file app/Http/Kernel.php):
protected $middleware = [ [...] //\App\Http\Middleware\TestMiddleware::class, \App\Http\Middleware\CorsMiddleware::class, ];
Preflight Request
Abbiamo appena sviluppato il nostro middleware, per aggiungere degli header ad una response. Per abilitare CORS manca tuttavia un ulteriore passaggio: lo sblocco delle preflight request.
Apriamo quindi il nostro file delle routes routes/web.php e aggiungiamo il seguente codice:
Route::options('/{path}', function(){ return ''; })->where('path', '.*');
Il codice in questione si occupa di definire una route, in grado di gestire tutte le richieste HTTP di tipo OPTION e ritornare (con codice 200) una response vuota. In particolare, il metodo where, ci consente di specificare un’espressione regolare per il nostro parametro “path” (per informazioni aggiuntive consulta la documentazione ufficiale).
Conclusione
Abbiamo abilitato CORS per Laravel, from scratch: utilizzando gli strumenti che ci mette a disposizione il framework. Esistono tuttavia dei package per abilitare CORS, senza dover mettere mano direttamente al codice: il mio consiglio è quello di dare un’occhiata al package di Barry vd. Heuvel che fornisce una soluzione perfettamente funzionante e altamente configurabile.