iT邦幫忙

2021 iThome 鐵人賽

DAY 7
0
Modern Web

用React刻自己的投資Dashboard系列 第 7

用React刻自己的投資Dashboard Day7 - CORS與Proxy Server

tags: 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.

跨來源資源共用(CORS)

  • MDN上對於CORS的說明

跨來源資源共用(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 資源。

  • MDN上面的說明圖,非常清楚

來看圖說故事一下,網頁所在的網域是 domain-a.com,那麼網頁向 domain-a.com (相同來源)發出的requests都會被允許;如果是向 domain-b.com (跨來源)發出的requests就會受到CORS的控管。

為什麼瀏覽器要有CORS

  • 因為安全性、安全性、安全性。

舉例來說,假設你經營一個電子郵件發送平台,讓使用者登入平台之後可以發送電子郵件給其他人。如果瀏覽器沒有CORS的限制,而且你的平台是會保留使用者的登入狀態的,那麼,在使用者已經登入的情況之下,如果使用者去到某一個惡意的網站,那麼這個網站不需要使用者同意就能夠對你的平台發出requests(就是跨來源請求)做任何動作,而你的平台是不會阻擋的。

上述的這個情況聽起來是有點危險的,因此CORS可以被視為是一種保護機制。

如何解決CORS阻擋的問題

解決CORS的方法有很多種,這邊就分享兩個狀況,以及較正常的解決方式:

  • 前後端分離的架構
    假設你的網站服務,前後端是在不同的domain之下,那麼比較合理的方式是從伺服器那邊調整HTTP Header設定,設定能夠接受的來源、方法及資訊。
  • 串接第三方API
    也就是這次我遇到的情況,在串接FRED API時,發現FRED是阻擋跨來源請求的,而我不可能去請FRED修改他們的伺服器的設定,基於CORS是瀏覽器的限制,那麼解決方法就是不要透過瀏覽器發出請求,而是透過一個代理伺服器發送請求,那麼這個請求就不會被擋。

自己寫一支proxy server

雖然這個系列主要是寫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下去吧!


上一篇
用React刻自己的投資Dashboard Day6 - 建立圖表區元件,串接API取得數據
下一篇
用React刻自己的投資Dashboard Day8 - useState hook
系列文
用React刻自己的投資Dashboard30

尚未有邦友留言

立即登入留言