用 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


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,天天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,天天都给你推送新鲜的前端技术文章

欢迎继续阅读本专栏其它高赞文章: