2021鐵人賽
React
上一篇在串接API的時候有遇到一個前端蠻常見的問題,跨來源資源共用(CORS)被阻擋,錯誤代碼如下:
打開瀏覽器的console會看到:
Access to fetch at 'https://api.stlouisfed.org/fred/series/observations?series_id=TREAST&api_key=xxxxxxxxxxxx&file_type=json' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
跨來源資源共用(Cross-Origin Resource Sharing (CORS))是一種使用額外 HTTP 標頭令目前瀏覽網站的使用者代理 (en-US)取得存取其他來源(網域)伺服器特定資源權限的機制。當使用者代理請求一個不是目前文件來源——例如來自於不同網域(domain)、通訊協定(protocol)或通訊埠(port)的資源時,會建立一個跨來源 HTTP 請求(cross-origin HTTP request)。
...中間省略
基於安全性考量,程式碼所發出的跨來源 HTTP 請求會受到限制。例如,XMLHttpRequest 及 Fetch 都遵守同源政策(same-origin policy)。這代表網路應用程式所使用的 API 除非使用 CORS 標頭,否則只能請求與應用程式相同網域的 HTTP 資源。
來看圖說故事一下,網頁所在的網域是 domain-a.com,那麼網頁向 domain-a.com (相同來源)發出的requests都會被允許;如果是向 domain-b.com (跨來源)發出的requests就會受到CORS的控管。
舉例來說,假設你經營一個電子郵件發送平台,讓使用者登入平台之後可以發送電子郵件給其他人。如果瀏覽器沒有CORS的限制,而且你的平台是會保留使用者的登入狀態的,那麼,在使用者已經登入的情況之下,如果使用者去到某一個惡意的網站,那麼這個網站不需要使用者同意就能夠對你的平台發出requests(就是跨來源請求)做任何動作,而你的平台是不會阻擋的。
上述的這個情況聽起來是有點危險的,因此CORS可以被視為是一種保護機制。
解決CORS的方法有很多種,這邊就分享兩個狀況,以及較正常的解決方式:
雖然這個系列主要是寫React系列,不過剛好有查到如何自己用node.js去架一個簡單的proxy server,就把它寫下來好了,完整程式碼請看GitHub repo。
程式主檔 App.js
const express = require('express');
const fetch = require('node-fetch');
// Create Express Server
const app = express();
// allow cors
const cors = require('cors');
app.use(cors());
// setting body-parser
app.use(express.urlencoded({ extended: true }))
// Proxy endpoints
app.use('/', function (req, res, next) {
const targetURL = req.header('Target-URL');
fetch(targetURL + req.url)
.then(res => res.json())
.then(json => {
res.json(json)
});
});
app.set('port', process.env.PORT || 5000);
// Start Proxy
app.listen(process.env.PORT || 5000, () => {
console.log(`Starting Proxy`);
});
可以發現程式非常的簡短,因為只是讓我的前端可以測試使用,只寫了基本的設定就將它發佈上heroku,比較重要的是cors的設定以及透過讀取request header的資訊,得知前端本來想要打的網址是哪一個,就透過heroku上的server向目標網站發出請求,再將取得的資料回傳給前端。
前端新手第一次遇到CORS阻擋會是很困惑的,很開心能夠藉由這一次的練習,對於CORS的原理及解決方式有更進一步的了解,接下來就回到原來的軌道,繼續React下去吧!