取得像是開放資料這類別人的資料時,常常會遇到 CORS 問題,因此無法取得資料。這種問題除了去請原資料提供者開放 CORS 以外,還有什麼解決方法呢?

Access to XMLHttpRequest at 'https://noob.tw/some-data' from origin 'https://test.noob.tw' has been blocked by CORS policy: No 'Access-Control-Allow-Origin header is present on the requested resource.

以開放資料來說,其實資料已經陸續開放出來就是一件很寶貴的事情了,再加上早期都是以 csv、xml 型式丟出來,或許也沒設想到會有人 AJAX 來使用吧。某種程度上,要求對方開放 CORS 不是一天兩天就能解決的問題。反倒,資料提供者是自己,倒是可以在 Server 上加上一些 header,讓你的資料能讓指定的 domain、或是任何來源去取得。

但今天的例子,討論的是開放資料。既然沒辦法從伺服器端下手,只好找別的做法了。

前情提要

順帶一提,這是 JS 地下城 的第五篇,設計稿在 這裡。而題目沒有提供資料來源,我選的資料是行政院環保署的 AQI 資料,並沒有開放 CORS 存取。

前端拿不到,那就讓後端來拿吧

CORS 其實只會擋這種前端 AJAX 的存取。你可以試試用 curl 來拿資料,是沒有問題的。

curl

那其實有個做法,可以架一個 Node.js 的 Server,透過 axios 拿資料、透過 koaexpress 的新版) 丟資料出來,然後再開放 CORS 即可。

const Koa = require('koa');
const axios = require('axios');
const cors = require('@koa/cors');

const app = new Koa();

app.use(cors());

app.use(async (ctx) => {
    const { data } = await axios.get('http://opendata.epa.gov.tw/ws/Data/AQI/?$format=json');
    ctx.body = data;
});

app.listen(3000);

這樣就可以寫好一個幫你拿資料,而且沒有 CORS 限制的 Server。

但是我們也不可能每需要用這個東西,就要架一次這個 Server。所以我們可以改使用 cors-anywhere 這個套件,讓你寫好一次,就能存取多份資料。下面這個是 cors-anywhere 提供的範例:

// Listen on a specific host via the HOST environment variable
var host = process.env.HOST || '0.0.0.0';
// Listen on a specific port via the PORT environment variable
var port = process.env.PORT || 8080;

var cors_proxy = require('cors-anywhere');
cors_proxy.createServer({
    originWhitelist: [], // Allow all origins
    requireHeader: ['origin', 'x-requested-with'],
    removeHeaders: ['cookie', 'cookie2']
}).listen(port, host, function() {
    console.log('Running CORS Anywhere on ' + host + ':' + port);
});

之後你就可以這樣用(也是由 cors-anywhere 提供的範例):

換句話說你就可以前往 http://localhost:8080/http://opendata.epa.gov.tw/ws/Data/AQI/?$format=json 拿資料了。

你可以寫好這樣的東西,把它推到自己的 server 或 Heroku 上,就可以自己使用。

沒有 Server 怎麼辦?

自己使用免費的 Heroku 可能有些限制,例如第一次使用可能會需要 20 秒喚醒,太久沒人使用也會進入休眠。這時候可以使用 DigitalOcean 架一個 VPS 主機,就能架好沒有限制的 CORS Proxy。

不過如果真的不想花錢的話,還是有一些免費的服務可以用。像是剛剛的 cors-anywhere 作者就有提供免費服務:https://cors-anywhere.herokuapp.com/ ,用法跟上面說的一樣。另外還有像是:

之類的服務可以使用。不過這類服務是免費的,有些有使用次數限制,有些不見得一直保持上線,如果是正式上線(production)的服務,建議還是要自己架個 server 來使用比較好。