用 Node.js 處理 CORS

做者:Janith Kasun

翻譯:瘋狂的技術宅javascript

原文:https://stackabuse.com/handli...html

未經容許嚴禁轉載前端

介紹

在本文中,咱們將研究怎樣用 Express 配置 CORS 以及根據須要定製 CORS 中間件。java

什麼是CORS

CORS 是「跨域資源共享」的簡寫。它是一種容許或限制向 Web 服務器上請求資源的機制,具體取決於進行 HTTP 請求的位置。node

這種策略用於保護特定 Web 服務器免受其餘網站或域的訪問。只有容許的域才能訪問服務器中的文件,例如樣式表、圖像或腳本等。程序員

假設你當前使用的是 http://example.com/page1,而且你引用的是來自 http://image.com/myimage.jpg 的圖片,那麼除非 http://image.com 容許與 http://example.com 進行跨域共享,不然將沒法獲取該圖像。面試

每一個 HTTP 請求頭中都有一個名爲 origin 的頭。它定義了域請求的來源。能夠用這個頭的信息來限制引用你服務器上的資源。數據庫

默認來自任何其餘來源的請求都會受到瀏覽器的限制。

例如當開發時若是用的是 React 或 Vue 這類的前端庫,則前端應用將運行在 http://localhost:3000 上,同時,你的 Express 服務器可能正在其餘端口上運行,例如 http://localhost:2020。這時就須要在這些服務器之間容許 CORS。express

若是你在瀏覽器控制檯中看到下圖這類的錯誤。問題可能出在 CORS 限制上:npm

image.png

若是咱們須要提供公共 API 並但願控制對某些資源的訪問和使用方式時,CORS 可以發揮很大的做用。

另外,若是想在其餘網頁上使用本身的 API 或文件,也能夠簡單地將 CORS 配置爲容許本身引用,同時把其餘人拒之門外。

用 Express 配置 CORS

首先建立一個新的項目,並建立目錄結構,而後使用默認設置運行 npm init

$ mkdir myapp
$ cd myapp
$ npm init -y

接下來安裝所需的模塊。咱們將使用 expresscors 中間件:

$ npm i --save express
$ npm i --save cors

而後,開始建立一個簡單的有兩個路由的 Web 程序,用來演示 CORS 的工做原理。

首先建立一個名爲 index.js 的文件,用來充當 Web 服務器,並實現幾個請求處理函數:

const express = require('express');
const cors = require('cors');

const app = express();

app.get('/', (req, res) => {
    res.json({
        message: 'Hello World'
    });
});

app.get('/:name', (req, res) => {
    let name = req.params.name;

    res.json({
        message: `Hello ${name}`
    });
});

app.listen(2020, () => {
    console.log('server is listening on port 2020');
});

運行服務器:

$ node index.js

訪問 http://localhost:2020/ 服務器應該返回 JSON 消息:

{
  "message": "Hello World"
}

訪問 http://localhost:2020/something 應該可以看到:

{
  "message": "Hello something"
}

啓用全部CORS請求

若是想爲全部的請求啓用 CORS,能夠在配置路由以前簡單地使用 cors 中間件:

const express = require('express');
const cors = require('cors');

const app = express();

app.use(cors())

......

若是須要,這會容許在網絡上的任何位置訪問全部路由。因此在本例中,每一個域均可以訪問兩條路由。

例如,若是咱們的服務器在 http://www.example.com 上運行並提供諸如圖片之類的內容,則咱們容許 http://www.differentdomain.com 之類的其餘域從 http://www.example.com 進行引。

所以 http://www.differentdomain.com 上的網頁能夠將咱們的域用做圖像的來源:

<img src="http://www.example.com/img/cat.jpg">

爲單個路由啓用 CORS

若是隻須要其中某一個路由,能夠在某個路由中將 cors 配置爲中間件:

app.get('/', cors(), (req, res) => {
    res.json({
        message: 'Hello World'
    });
});

這會容許任何域訪問特定的路由。在當前的狀況下,其餘域都只能訪問 / 路由。僅在與 API(在本例中爲http://localhost:2020)的相同域中發起的請求才能訪問 /:name 路由。

若是嘗試另外一個來源發送請求到 / 路徑將會成功,而且會收到 Hello World 做爲響應:

fetch('http://localhost:2020/')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(err => console.error(err));

運行上面的代碼,會看到來自服務器的響應已成功輸出到控制檯:

{
    message: 'Hello World'
}

若是訪問除根路徑之外的其餘路徑,例如 http://localhost:2020/namehttp://localhost:2020/img/cat.png,則此請求將會被瀏覽器阻止:

fetch('http://localhost:2020/name/janith')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

若是在其餘 Web 應用中運行代碼,應該看到如下錯誤:

image.png

用選項配置CORS

還能夠用自定義選項來配置 CORS。能夠根據須要配置容許的 HTTP 方法,例如 GETPOST

下面是經過 CORS 選項容許單個域訪問的方法:

var corsOptions = {
    origin: 'http://localhost:8080',
    optionsSuccessStatus: 200 // For legacy browser support
}

app.use(cors(corsOptions));

若是你在源中配置域名-服務器將容許來自已配置域的CORS。所以,在咱們的例子中,能夠從 http://localhost:8080 訪問該API,並禁止其餘域使用。

若是發送一個 GET 請求,則任何路徑都應該能夠訪問,由於這些選項是在應用在程序級別上的。

運行下面的代碼將請求從 http://localhost:8080 發送到 http://localhost:2020

//
fetch('http://localhost:2020/')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

//
fetch('http://localhost:2020/name/janith')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));

能夠看到被容許從該程序和域中獲取信息。

還能夠根據須要配置容許的 HTTP 方法:

var corsOptions = {
    origin: 'http://localhost:8080',
    optionsSuccessStatus: 200 // 對於舊版瀏覽器的支持
    methods: "GET, PUT"
}

app.use(cors(corsOptions));

若是從 http://localhost:8080 發送POST請求,則瀏覽器將會阻止它,由於僅支持 GET 和 PUT:

fetch('http://localhost:2020', {
  method: 'POST',
  body: JSON.stringify({name: "janith"}),
})
.then(response => response.json())
.then(data => console.log(data))
.catch(err => console.error(err));

用函數配置動態 CORS 源

若是配置不知足你的要求,也能夠建立函數來定製 CORS。

例如假設要容許 http://something.comhttp://example.com.jpg 文件進行CORS共享:

const allowlist = ['http://something.com', 'http://example.com'];

    const corsOptionsDelegate = (req, callback) => {
    let corsOptions;

    let isDomainAllowed = whitelist.indexOf(req.header('Origin')) !== -1;
    let isExtensionAllowed = req.path.endsWith('.jpg');

    if (isDomainAllowed && isExtensionAllowed) {
        // 爲此請求啓用 CORS
        corsOptions = { origin: true }
    } else {
        // 爲此請求禁用 CORS
        corsOptions = { origin: false }
    }
    callback(null, corsOptions)
}

app.use(cors(corsOptionsDelegate));

回調函數接受兩個參數,第一個是傳遞 null 的錯誤,第二個是傳遞 { origin: false } 的選項。第二個參數能夠是用 Express 的 request 對象構造的更多選項。

因此 http://something.comhttp://example.com 上的 Web 應用將可以按照自定義配置從服務器引用擴展名爲 .jpg 的圖片。

這樣能夠成功引用資源文件:

<img src="http://yourdomain.com/img/cat.jpg">

可是下面的文件將會被阻止:

<img src="http://yourdomain.com/img/cat.png">

從數據源加載容許的來源列表做

還能夠用保存在數據庫中的白名單列表或任何一種數據源來容許 CORS:

var corsOptions = {
    origin: function (origin, callback) {
        // 從數據庫加載容許的來源列表
        // 例如:origins = ['http://example.com', 'http//something.com']
        database.loadOrigins((error, origins) => {
            callback(error, origins);
        });
    }
}

app.use(cors(corsOptions));

173382ede7319973.gif


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索