喔不!是CORS
前言
看到JS地下城5F題目的時候,心裡想「噢,這不就AJAX嗎 簡單!」
,到了實際上要抓資料的時候,發現並沒有那麼容易,一開始是透過ES6內建的fetch()來抓資料,但跳出了這個訊息
問題所在
這個訊息說明了行政院環保署的Server端不提供CORS(Cross-Origin Resource Sharing)存取。
這時內心又會有另一個問題,CORS是甚麼? 這裡引用一段MDN上的說明
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell a browser to let a web application running at one origin (domain) have permission to access selected resources from a server at a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, and port) than its own origin.
簡單來說,XMLHttpRequest
和Fetch API
兩種AJAX的存取方式都需要遵守same-origin policy,也就是在前端呼叫另一個網域的資源需要在同一個域名下才行,例如fetch()不能在 http://127.0.0.1/ 底下呼叫 https://www.google.com/ 的資源。
雖然這兩個AJAX的呼叫方法都要遵守same-origin policy,但可以透過在伺服器上設定加入回應Access-Control-Allow-Origin
的header,讓其他網域能夠存取你伺服器上的資源。(氣象局API不能直接存取的原因就是少了這個header)
解決方法
上面有提到前端的AJAX不能直接使用GET存與資料,但後端可以,所以我們的解決方法是透過自己寫的代理server來取得氣象局的AQI資料後,再自己透過HTTP將取得的資料回傳回來。
這裡使用了request來GET網頁上的資料,並使用express透過HTTP回傳回來
const request = require('request');
var express = require('express');
var app = express();
app.get(/^\/corskiller\/.*/, async function (req, res) {
const targetURL = req.originalUrl.slice(12);
await request(targetURL, async function (error, response, body) {
await res.setHeader('Access-Control-Allow-Origin', '*')
await res.setHeader('Content-Type', 'application/json');
await res.send(body);
});
});
app.listen(3000, function () {
console.log('Waiting for GET');
});
架好之後只要透過正常的AJAX抓取https://localhost:3000/corskiller/<API URL>
,就能獲得該API上的資料囉~
最重要的Access-Control-Allow-Origin
設定伺服器的可以允許哪些網域來存取,在這裡設為無限制
await res.setHeader('Access-Control-Allow-Origin', '*')
其他設定參數如下
- 不限制 Access-Control-Allow-Origin : *
- 特定域名才能存取 Access-Control-Allow-Origin : domain
- 不啟用 Access-Control-Allow-Origin : null
之後的事情
弄了這麼一大坨之後,開開心心部屬到GCP上面,結果發現域名沒有SSL憑證,不能直接存取,覺得心好累,覺得heroku會休眠有點小討厭,所以就弄個Google Script來處理這個問題了_(:з」∠)_
參考資料
CORS -
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Access-Control-Allow-Origin -
https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Server-Side_Access_Control