用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下去吧!


<<:  Day7 用python写UI-聊聊标签Label方法(二)

>>:  Day14 Example of Backfill

JavaScript入门 Day03_输出文字

那今天终於要来开始打code了!! 就从最基本的 Hello World开始吧 每一款程序语言最基本...

【程序】软件测试 Testing 转生成恶役菜鸟工程师避免 Bad End 的 30 件事 - 20

测试 为什麽要写测试 何时该订定测试 和哪些人协作 ...

[Day29] Cost Plaining

终於到了倒数第二篇文章了,今天来介绍云端付费相关的计画,透过一些小技巧,可以减少在云端上的很多开销哦...

[Day 14] 关於 SRE 与 SEC 的关系

关於SEC的事情 资料库演练100%备份还原 每年至少两次的资料灾害恢复演练,资料要100%覆盖,要...

Day19 Redis架构实战-持久化AOF

Redis持久化 Redis持久化模式->AOF AOF (Append Only File)...