介绍
在本文中,我们将研究什么是CORS,如何使用Express配置CORS,以及如何根据需要定制CORS中间件。
什么是CORS
CORS是跨域资源共享的简写。它是一种机制,允许或限制Web服务器上请求的资源,具体取决于启动HTTP请求的位置。
此策略用于保护特定Web服务器免受其他网站或域的访问。例如,只有允许的域才能访问服务器中的托管文件,例如样式表,图像或脚本。
如果您当前正在使用http://example.com/page1
并且要从中引用图像http://image.com/myimage.jpg
,则除非http://image.com
允许使用进行跨域共享,否则http://example.com
将无法获取该图像。
origin
每个HTTP请求中都有一个HTTP标头。它定义了域请求的来源。我们可以使用标头信息来限制或允许来自Web服务器的资源来保护它们。
默认情况下,来自任何其他来源的请求都将受到浏览器的限制。
例如,当您仍处于开发阶段时-如果您使用的是诸如React之类的前端库,则将在上使用您的前端应用程序http://localhost:3000
。同时,您的Express服务器可能正在其他端口上运行,例如http://localhost:2020
。
因此,您需要在这些服务器之间允许CORS。
如果您在浏览器控制台中看到此常见错误。CORS限制可能是问题所在:
当您提供公共API并希望控制对某些资源的访问以及人们的使用方式时,CORS确实很有用。
另外,如果您想在其他网页上使用自己的API或文件,则可以简单地将CORS配置为允许这样做,同时仍将其他人拒之门外。
使用Express配置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,则只需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'
});
});
这将允许任何域都可以访问特定路由。因此,在您的情况下,/
每个域都只能访问该路由。/:name
只有在与API相同的域中发起的请求(http://localhost:2020
在我们的示例中)才可以访问该路由。
例如,如果您尝试将获取请求/
从其他来源发送到路径-它将成功,并且您将获得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
您还可以将配置选项与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));
// Or
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 // For legacy browser support
methods: "GET, PUT"
}
app.use(cors(corsOptions));
如果我们POST
从发送请求http://localhost:8080
,则该请求只会被浏览器阻止,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。
例如,假设您要允许CORS共享.jpg
文件http://something.com
和http://example.com
:
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) {
// Enable CORS for this request
corsOptions = { origin: true }
} else {
// Disable CORS for this request
corsOptions = { origin: false }
}
callback(null, corsOptions)
}
app.use(cors(corsOptionsDelegate));
回调函数将接受两个参数。第一个是我们通过的错误,null
第二个是我们通过的选项{ origin: false }
。第二个参数可能是使用request
Express请求处理程序中的对象构造的许多选项。
因此,在自定义功能中配置的,托管在Web应用程序上http://something.com
或http://example.com
能够.jpg
从服务器引用带有扩展名的图像的Web应用程序。
因此,以下任何一个都将成功完成以下图像附件:
<img src="http://yourdomain.com/img/cat.jpg">
但是以下附件将被阻止:
<img src="http://yourdomain.com/img/cat.png">
从数据源加载允许的来源清单
您还可以使用数据库中允许的域的列表或使用任何后备数据源来允许CORS:
var corsOptions = {
origin: function (origin, callback) {
// Loading a list of allowed origins from the database
// Ex.. origins = ['http://example.com', 'http//something.com']
database.loadOrigins((error, origins) => {
callback(error, origins);
});
}
}
app.use(cors(corsOptions));
结论
在本文中,我们介绍了CORS是什么以及如何使用Express对其进行配置。然后,我们为所有请求,特定请求,添加的选项和限制设置了CORS,并为动态CORS配置定义了自定义功能。