CORS, Richiesta di Preflight e Metodo OPTIONS
Scritto da Francesco Di Donato 10 aprile 2022 4 minuti di lettura
Prima di tutto, devi essere a conoscenza della Politica di stessa origine 🔗. È un meccanismo integrato nel browser che limita come uno script su un’origine interagisce con una risorsa su un’altra.
Stessa origine 🏠🏠
Iniziamo con il caso più semplice. Sia il server che detiene la risorsa sia il client che la richiede risiedono sotto la stessa origine 🔗 (<scheme>://<hostname><port>).
Il server è esposto su http://localhost:3000 e risponde a HTTP GET / restituendo un file HTML.
Per brevità, gli esempi di codice mostrano solo ciò su cui voglio concentrarmi. Se qualcosa non è chiaro, usa i vari Codice sorgente accompagnatorio (stessa origine) 🔗 che troverai.
server.get("/", (_, reply) => {
reply.sendFile("index.html");
});
server.get("/me", (_, reply) => {
reply.status(200).send({ nickname: "didof" });
});
L’HTML arrivato sul client chiede al server sulla stessa origine la risorsa associata al percorso /me e mostra il soprannome sullo schermo.
<p>Soprannome: <output></output></p>
<script>
// Questo gira su http://localhost:3000
fetch("/me") // fetch("http://localhost:3000/me")
.then((res) => res.json())
.then((res) => {
document.querySelector("output").textContent = res.nickname;
});
</script>
Il payload è accessibile in quanto viene rispettata la politica di stessa origine. Non c’è bisogno di CORS, non c’è bisogno di richiesta di preflight.
Cross-origin 🏠🏭
Ora il client è esposto su http://localhost:8000 mentre il server con la risorsa è esposto su http://localhost:3000. Anche se schema e hostname sono gli stessi, le porte sono diverse - quindi sono due origini diverse.
Nel mondo reale, il seguente concetto si verifica quando il client su
https://mywebsite.comrichiede una risorsa ahttp://api.foo.it/bar
index.html
<script>
// Questo gira su http://localhost:8000
const request = new Request("http://localhost:3000/me", {
method: "GET",
});
fetch(request)
.then((res) => res.json())
.then((res) => { ... })
</script>
Come puoi immaginare, hai un errore di CORS 🔗.
L’errore nella console spiega chiaramente il problema. Correggiamolo. Sul server, gestiamo CORS.
server.register(fastifyCors, {
origin: "http://localhost:8000",
});
Qui uso fastify 🔗, ma qualsiasi framework offre il proprio modo di gestire CORS. Sono semplicemente intestazioni aggiunte alla risposta, nient’altro - puoi naturalmente aggiungerle manualmente.
Ora, la risposta includerà l’access-control-allow-origin e il valore sarà http://localhost:8000.
Il server sta fondamentalmente dando il permesso al browser che ha emesso la richiesta di aprire la risposta.
Richiesta di Preflight 🏠✉️🏭
Ci sono due tipi di richiesta CORS:
- Richiesta semplice
- Richiesta di preflight
Quale viene utilizzata è determinado dal browser. Fino a questo momento il client ha effettuato richieste semplici perché si adattano ai criteri 🔗.
Per lo scopo di questo post, devi solo sapere che per forzare il browser a effettuare una richiesta di preflight devi semplicemente aggiungere alla richiesta un intestazione personalizzata.
<script>
const headers = new Headers();
headers.append("X-CUSTOM-HEADER", "foo");
// Questo gira su http://localhost:8000
const request = new Request("http://localhost:3000/me", {
method: "GET",
headers,
});
fetch(request)
.then((res) => res.json())
.then((res) => { ... })
E questo è sufficiente perché il browser lanci due richieste invece di una.
Il metodo utilizzato è OPTIONS 🔗, che è interpretato dal server come una richiesta di informazioni sul request url definito.
Il browser aggiunge anche alcune intestazioni alla richiesta di preflight. Access-Control-Request-Headers e Access-Control-Request-Method con i loro valori relativi.
Il browser sta chiedendo permesso al server per effettuare una richiesta GET che ha tra le varie intestazioni anche un certo X-CUSTOM-HEADER.
Questo particolare server, configurato per essere estremamente permissivo, risponde con access-control-allow-headers il cui valore rispecchia quello richiesto e access-control-allow-methods che dice che tutti i metodi sono consentiti.
Sta dando un grande pollice in su 👍. Il browser può quindi eseguire la richiesta desiderata.
Ovviamente, se per qualche motivo il server fosse istruito a non consentire la presenza dell’intestazione personalizzata, riceveresti un errore.
Come detto, questo è solo una semplice chiacchierata. Se sei interessato, di seguito ci sono alcuni riferimenti per approfondire. Io 💖 l’H di HTML!