在Express中处理CORS

如何通过设置CORS允许跨站点请求

在浏览器中运行的JavaScript应用程序通常只能从为其提供服务的同一域(源)中访问HTTP资源。

始终可以加载来自相同来源的图像或脚本/样式。另外,使用@font-face默认情况下设置了“相同来源”政策。与其他不那么受欢迎的事物(例如WebGL纹理和drawImageCanvas API中加载的资源)。

但是,对外部第三方服务器的XHR和Fetch调用将失败。除非第三方服务器实施允许建立连接并允许下载和使用所请求资源的机制。

这种机制称为CORS跨域资源共享

需要CORS的一件非常重要的事情是ES模块,最近在现代浏览器中引入。

如果您未设置CORS政策在服务器上使其可以服务于第三方来源,该请求将失败。

获取示例:

Fetch failed because of CORS policy

XHR示例:

XHR request failed because of CORS policy

跨域资源在以下情况下会失败:

  • 换一个领域
  • 换一个子域
  • 换一个港口
  • 换一个协议

CORS可以确保您的安全,以防止恶意用户利用您恰巧正在使用的任何Web平台。

如果同时控制两个服务器对于客户,您知道双方都是值得信赖的,因此有充分的理由允许资源共享。

如何?

这取决于您的服务器端堆栈。

浏览器支持

相当不错(基本上除IE <10以外的所有内容):

CORS browser support

Express的例子

如果您使用Node.js和Express作为框架,请使用CORS中间件包

这是Express Node.js服务器的简单实现:

const express = require('express')
const app = express()

app.get(’/without-cors’, (req, res, next) => { res.json({ msg: ‘😞 no CORS, no party!’ }) })

const server = app.listen(3000, () => { console.log(‘Listening on port %s’, server.address().port) })

如果你打/without-cors如果从其他来源获取请求,则会引发CORS问题。

您需要做的就是使事情顺利进行,cors上面链接的软件包,并将其作为中间件函数传递给端点请求处理程序:

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

app.get(’/with-cors’, cors(), (req, res, next) => { res.json({ msg: ‘WHOAH with CORS it works! 🔝 🎉’ }) })

/* the rest of the app */

我做了一个简单的Glitch示例,下面是它的代码:https://glitch.com/edit/#!/flavio-cors-client

这是Node.js Express服务器:https://glitch.com/edit/#!/flaviocopes-cors-example-express

请注意如何仍然收到由于服务器未正确处理CORS标头而导致失败的请求。正如您在“网络”面板中看到的那样,您可以在其中看到服务器发送的消息:

No response from CORS

仅允许特定来源

但是,此示例有一个问题:服务器将把任何请求作为跨域接受。

正如您在“网络”面板中看到的那样,传递的请求具有一个响应标头access-control-allow-origin: *

The CORS response header

您需要将服务器配置为仅允许一个来源服务,并阻止所有其他来源。

使用相同cors节点库,方法如下:

const cors = require('cors')

const corsOptions = { origin: https://yourdomain.com, }

app.get(’/products/:id’, cors(corsOptions), (req, res, next) => { //… })

您还可以提供更多服务:

const whitelist = ['http://example1.com', 'http://example2.com']
const corsOptions = {
  origin: function (origin, callback) {
    if (whitelist.indexOf(origin) !== -1) {
      callback(null, true)
    } else {
      callback(new Error('Not allowed by CORS'))
    }
  },
}

飞行前

有些请求以“简单”的方式处理。全部GET请求属于该组。

一些 POSTHEAD请求也是如此。

POST如果请求满足使用Content-Type的要求,则该请求也属于此组

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

所有其他请求都必须经过称为预检的预批准阶段。浏览器通过发出以下命令来确定其是否有权执行操作:OPTIONS要求。

预检请求包含一些头,服务器将使用这些头来检查许可权(忽略了不相关的字段):

OPTIONS /the/resource/you/request
Access-Control-Request-Method: POST
Access-Control-Request-Headers: origin, x-requested-with, accept
Origin: https://your-origin.com

The server will respond with something like this (irrelevant fields omitted):

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://your-origin.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE

We checked for POST, but the server tells us we can also issue other HTTP request types for that particular resource.

Following the Node.js Express example above, the server must also handle the OPTIONS request:

var express = require('express')
var cors = require('cors')
var app = express()

//allow OPTIONS on just one resource app.options(’/the/resource/you/request’, cors())

//allow OPTIONS on all resources app.options(’*’, cors())

Download my free Express.js Handbook


More express tutorials: