做者:Janith Kasun翻譯:瘋狂的技術宅javascript
原文:https://stackabuse.com/handli...html
未經容許嚴禁轉載前端
在本文中,咱們將研究怎樣用 Express 配置 CORS 以及根據須要定製 CORS 中間件。java
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
若是咱們須要提供公共 API 並但願控制對某些資源的訪問和使用方式時,CORS 可以發揮很大的做用。
另外,若是想在其餘網頁上使用本身的 API 或文件,也能夠簡單地將 CORS 配置爲容許本身引用,同時把其餘人拒之門外。
首先建立一個新的項目,並建立目錄結構,而後使用默認設置運行 npm init
:
$ mkdir myapp $ cd myapp $ npm init -y
接下來安裝所需的模塊。咱們將使用 express
和 cors
中間件:
$ 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
中間件:
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
配置爲中間件:
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/name
或 http://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 應用中運行代碼,應該看到如下錯誤:
還能夠用自定義選項來配置 CORS。能夠根據須要配置容許的 HTTP 方法,例如 GET
和 POST
。
下面是經過 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。
例如假設要容許 http://something.com 和 http://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.com
或 http://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));