Cross-Origin Resource Sharing (CORS) 核心原理
初學者學習網頁或是瀏覽器相關知識的時候,常會想寫一個靜態的 html 去存取本地端自己電腦裡面的檔案,例如讀取圖片並顯示。我原本天真以為這只是一個簡單的 HTTP 呼叫本地檔案就可以解決,生成一個 html 就可以跑了,殊不知這比想像中的還難實現。因為瀏覽器有些安全政策不讓你輕鬆存取本地檔案。仔細想想也是合理,畢竟當你不小心開一個 html 檔案,卻發現這個 html 把你的檔案都偷讀了一遍,也是會蠻意外的。
有一些替代方案,像是使用瀏覽器提供的 FileReader 介面。但這變成要使用者選取檔案搭配 <input type="file">
服用,不太自動化。並不是真正意義上的自動讀取。
雖然 <script src="{URL}">
跟 <link href="{URL}" />
可以不管三七二十一的取得本機的檔案,但這些應用的場景很不全面,一個拿 JavaScript 一個拿 CSS 檔案。其他類型檔案就沒辦法拿了。
一開始我嘗試使用 JavaScript fetch API 取得本地的檔案,簡單粗暴,但在 Chrome 因為安全性的原因會失敗。如果今天一個網站執行了 JavaScript 之後你的電腦中的資料就全部被讀取走,是否很可怕?
那所以說那個錯誤訊息裡面 CORS request 是什麼意思?
查下去發現是個漫天大坑。
簡短的說
Cross-Origin Resource Sharing (CORS) 是一個針對瀏覽器安全性的實作規格。它定義了某些 headers ,讓 server 可以標明哪些 origin 是可以被跨站訪問的。對於 “某些請求”,通常是那些有可能對 server 有潛在不良影響的請求,他規定瀏覽器根據某些流程取得某些 response headers 檢驗通過才能繼續,否則會顯示請求失敗。
首先,”某些請求” 是指什麼? 這就有點模糊。我並沒有找到太正式的定義。
以下舉幾個例子。<a>
, <img>
, <script>
, <link>
這種內嵌的資源通常都不需要特殊流程就可訪問。
然而, JavaScript 內發請的請求通常就需要過特殊流程才能訪問。
對於那些需要通過 CORS 特殊流程的請求,檢驗方式又可分為兩種。一種需要 preflight 發一個 OPTIONS 請求檢查,一種不用。
Simple Request
不用 pre-flight 的請求,瀏覽器會在發送請求的時候帶個 header 叫做 origin 。舉例來說,當我在 google.com 的網頁上,使用 JavaScript 發起一個發送到 api.github.com 的請求。
fetch(‘https://api.github.com')
瀏覽器接收到 access-control-allow-origin: *
就知道 api.github.com 允許跨站訪問。因此可以成功收到 200 請求。
接下來是個失敗的範例。
fetch('https://www.youtube.com/')
如果不看 devtools console ,發生了什麼事根本就不知道。
哪些請求需要,哪些請求不需要 preflight 又是另一個故事了。滿足以下 simple requests 條件的就不需要 preflight 。
Preflight request
另外一種請求,需要 pre-flight 。如果要測試需要 preflight 的請求,可以使用下列當範例。
fetch(‘https://api.github.com', {headers: {
‘Content-Type’: ‘text/p123’
}})
可以看到一個 OPTIONS
請求先飛出去,再來才是真正的請求。
搞懂 CORS 的觀念後,你就會知道,當你遇到這個錯誤的時候,純粹就是瀏覽器在搞你。但要解決這個錯誤,需要從 server 端下手,加上適當的 CORS header。
CORS 實際操作部分我會想辦法再生一篇文章出來講。