CORS in Dettaglio

C

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.

Se sei uno sviluppatore web, certamente ti sei  scontrato con questo errore .. e di solito il primo incontro con il CORS è terribile.

Spesso i programmatori non vogliono comprendere il meccanismo dietro al  CORS pensando che aggiungendo l header

Access-Control-Allow-Origin: *

ad ogni risposta del webservice risolva l’errore, ma purtroppo non è sempre vero!

DEFINIZIONE

Si parla di  cross-origin HTTP request (CORS) quando un client richiede una risorsa di un differente dominio, protocollo o porta. Ad esempio una web application con dominio X non può richiedere una risorsa ad un dominio Y tramite AJAX request se Y non ha abilitato il CORS.

Panoramica

Lo standard CORS funziona aggiungendo un nuovo HTTP headers che permette ai server di specificare i domini  a cui è permesso fare richieste per recuperare dati. E’ possibile anche inserire come dominio abilitato * per abilitare qualsiasi dominio.

Access-Control-Allow-Origin: *

Leggendo quanto scritto fino adesso  potrebbe sembrare che sia necessario includere l’header sopra per risolvere il problema e abilitare un webserver a rispondere a client di qualsiasi dominio; questo, come già anticipato, non è vero. Per mostrarvelo vi riporto un classico scenario nel mondo delle web application:

Supponiamo di avere un backend REST deployato su un dominio X e una applicazione frontend deployata in un altro dominio Y ( o un client mobile ibrido). Il backend rest è configurato per includere in ogni risposta l’header:

Access-Control-Allow-Origin: *

Supponiamo che il backend RESTful metta a disposizione una API REST così strutturata:

  • Url: http:X/order
  • metodo POST
  • body della richiesta atteso il json che rappresenta l ordine da inserire

Quando il frontend esegue la richiesta AJAX riassunta sopra accade qualcosa di strano, viene sollevato l’errore

Request Method:OPTIONS
Status Code:404 Not Found

Utilizzando la console di Chrome si nota che sulla rete ha girato una richiesta OPTIONS verso il server e non una richiesta POST; che significa tutto questo ?  Per comprendere cosa è accaduto occorre capire come funziona il protocollo del CORS.

cors in dettaglio

Oltre al controllo del dominio il CORS effettua controlli aggiuntivi per una tipologia di richieste; le Preflight request. Infatti per lo standard  CORS esistono due tipologie di richieste: Simple Request e Prefilight Request.

Simple Request

Una simple request è una richiesta http con uno di questi metodi:

  • GET
  • HEAD
  • POST

Gli unici header pemessi per una simple request sono:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (but note the additional requirements below)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width

Gli unici valori per l header Content-Type sono:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

Questo significa che una richiesta POST con un json nel body (application/json content type) non è una simple request.

Le richieste semplice sono correttamente gestite se il server include l’header Access Control allow origin specificando il dominio del client che richiede il servizio o in alternativa se il server ha specificato come domini accettati * . Una richiesta che non soddisfa i requisiti di una richiesta semplice è definita come Preflight Request e viene sottoposta a controlli aggiuntivi.

Preflight Request

Una preflight request è una richiesta che non soddisfa i requisiti riportati nel paragrafo precedente; Ad esempio una POST con content json.  In questi casi per prima cosa il browser invia una richiesta HTTP con metodo OPTIONS method al server, e quest’ultimo deve validare la richiesta OPTIONS ricevuta per rispondere se la richiesta che seguirà la OPTIONS è permessa o no; il client invierà la richiesta http solo ed esclusivamente se il server ha risposto 200 (ok) alla richiesta OPTIONS. Normalmente questo tipo di richieste sono validate tramite questo meccanismo perchè possono alterare i dati di un server ( ad esempio la nostra richiesta descritta ad inizio articolo era una POST per inserire un ordine in un server remoto).

Per descrivere meglio il meccanismo riporto l’esempio di  https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS che descrive lo  scenario.

Conclusione

Il motivo dell’errore riportato nel nostro esempio ( OPTION 404 NOT FOUND) era quindi causato dal fatto che la richiesta POST era stata identificata come preflight request e quindi il browser ha prima inviato una richiesta OPTIONS per sapere se poteva inoltrare una richiesta POST ma il server non era correttamente configurato per accettare richieste OPTIONS. Serve quindi sul backend un controller che intercetti richieste OPTIONS per rispondere 200 (OK) specificando tramite appositi header i metodi e header accettati. Per semplicità il controller che intercetta le richieste OPTIONS potrebbe rispondere sempre nel seguente modo:

HTTP STATUS 200
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS,
Access-Control-Allow-Headers: Content-Type

I framework più famosi hanno comunque delle facility e configurazioni per abilitare con pochi sfrorzi il  controller per la gestione di richieste OPTIONS e per includere in automatico l’header per i domini abilitati.

BIBLIOGRAFIA

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

A proposito di me

Dario Frongillo

Software architect con un forte background in Java, Architettura REST, framework Spring , Database Design,progettazione e sviluppo di SPA e RIA Web application con framework Javascript. Attualmente mi occupo di sviluppo soluzioni software in ambito Banking e Finance: in particolare progettazione e sviluppo di applicativi web based per la realizzazione di sistemi di trading, interfacce con i mercati finanziari e di servizi real time.

Gli articoli più letti

Articoli recenti

Commenti recenti