Abilitare CORS su Laravel

A

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.

Middleware Example

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,
];
Aggiungere il nostro middleware, all’array $middleware, equivale a registrarlo a livello globale (per ogni richiesta). Questa non è l’unica modalità, messa a disposizione da Laravel, per registrare i middleware. Durante lo sviluppo di un’applicazione Laravel, ad esempio, potrebbe essere utile definire alcuni gruppi di middleware da associare alle varie route (a seconda del tipo di risorsa che dovrà essere servita). Per informazioni aggiuntive sulla registrazione dei middleware consultare la documentazione ufficiale.

 

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.

A proposito di me

Paolo Rizzello

Nato e cresciuto in un'azienda di informatica, ha (inevitabilmente) intrapreso la strada dell'IT con un diploma da perito informatico prima e una laurea in ingegneria informatica poi, facendo, della sua passione, un lavoro a tutti gli effetti.
Si guadagna da vivere sviluppando web application in Laravel e ASP.NET... ma ha un oscuro passato di sviluppatore C++.

Di Paolo Rizzello

Gli articoli più letti

Articoli recenti

Commenti recenti