Express 中 CORS 跨域问题解决教程

一、CORS 核心概念

1. 同源策略

同源指的是 协议、域名、端口 三者完全一致。例如:

  • 前端地址:http://localhost:3000
  • 后端地址:http://localhost:8080
    由于端口不同,属于不同源,前端请求后端接口时会被浏览器拦截。

2. 跨域请求分类

  • 简单请求:满足以下条件的请求为简单请求

    1. 请求方法为 GET/HEAD/POST
    2. 请求头仅包含 Accept/Accept-Language/Content-Language/Content-Type(值仅限 application/x-www-form-urlencoded/multipart/form-data/text/plain
      简单请求会直接发送,浏览器在响应头中检查 Access-Control-Allow-Origin 字段判断是否允许跨域。
  • 复杂请求 :不满足简单请求条件的请求(如 PUT/DELETE 方法、自定义请求头)

    复杂请求会先发送 OPTIONS 预检请求,询问后端是否允许该跨域请求,预检通过后才会发送真实请求。

二、安装 cors 中间件

cors 是 Express 的官方推荐跨域处理中间件,通过 npm 安装:

bash 复制代码
npm install cors

三、基础使用方案

1. 允许所有来源跨域(开发环境常用)

这是最简便的配置,直接启用 cors 中间件,允许所有域名访问后端接口:

javascript 复制代码
var express = require('express')
var cors = require('cors')
var app = express()

// 全局启用 CORS,允许所有 origin
app.use(cors())

app.get('/api/data', (req, res) => {
  res.json({ message: 'This is CORS-enabled for all origins!' })
})

app.listen(8080, () => {
  console.log('Server running on port 8080')
})

适用场景:本地开发环境,无需限制访问来源。

2. 仅允许单个路由跨域

如果不需要全局跨域,可针对特定路由单独启用 cors

javascript 复制代码
var express = require('express')
var cors = require('cors')
var app = express()

// 仅 /api/products 路由允许跨域
app.get('/api/products/:id', cors(), (req, res) => {
  res.json({ message: 'This is CORS-enabled for a Single Route' })
})

// 其他路由不允许跨域
app.get('/api/user', (req, res) => {
  res.json({ message: 'No CORS for this route' })
})

app.listen(8080)

四、高级配置方案

1. 限制指定来源跨域(生产环境常用)

生产环境中,需要严格限制允许跨域的前端域名,避免恶意请求:

javascript 复制代码
var express = require('express')
var cors = require('cors')
var app = express()

// 配置跨域选项
var corsOptions = {
  // 仅允许 http://example.com 访问
  origin: 'http://example.com',
  // 兼容 IE11 等老旧浏览器(避免 204 状态码报错)
  optionsSuccessStatus: 200
}

// 全局启用带配置的 CORS
app.use(cors(corsOptions))

app.get('/api/data', (req, res) => {
  res.json({ message: 'Only example.com can access this!' })
})

app.listen(8080)

2. 动态白名单跨域

如果需要允许多个域名访问,可配置白名单,通过函数动态判断请求来源是否合法:

javascript 复制代码
var express = require('express')
var cors = require('cors')
var app = express()

// 跨域白名单
var whitelist = ['http://example1.com', 'http://example2.com', 'http://localhost:3000']

var corsOptions = {
  origin: function (origin, callback) {
    // 白名单内的 origin 或无 origin(如 Postman 等工具请求)允许跨域
    if (whitelist.indexOf(origin) !== -1 || !origin) {
      callback(null, true)
    } else {
      callback(new Error('Not allowed by CORS'))
    }
  }
}

app.use(cors(corsOptions))

app.get('/api/data', (req, res) => {
  res.json({ message: 'Only whitelisted domains can access!' })
})

app.listen(8080)

关键说明!origin 用于兼容 Postman、curl 等无浏览器环境的工具请求,避免这些工具被误判为跨域。

3. 处理复杂请求的预检(OPTIONS)

对于 PUT/DELETE 等复杂请求,需要手动配置 OPTIONS 预检请求的处理:

javascript 复制代码
var express = require('express')
var cors = require('cors')
var app = express()

// 全局处理所有路由的 OPTIONS 预检请求
app.options('*', cors())

// DELETE 方法属于复杂请求,需预检
app.delete('/api/data/:id', cors(), (req, res) => {
  res.json({ message: 'DELETE request is CORS-enabled!' })
})

app.listen(8080)

核心原理app.options('*', cors()) 会拦截所有 OPTIONS 预检请求,返回跨域允许的响应头,确保后续真实请求能正常发送。

4. 异步配置跨域

如果需要从数据库或配置文件中动态获取白名单,可使用异步配置:

javascript 复制代码
var express = require('express')
var cors = require('cors')
var app = express()

// 模拟从数据库获取白名单
function getWhitelistFromDB() {
  return Promise.resolve(['http://example1.com', 'http://example2.com'])
}

// 异步跨域配置
var corsOptionsDelegate = async function (req, callback) {
  var whitelist = await getWhitelistFromDB()
  var corsOptions = { origin: false }

  if (whitelist.indexOf(req.header('Origin')) !== -1) {
    corsOptions.origin = true
  }

  callback(null, corsOptions)
}

app.get('/api/data', cors(corsOptionsDelegate), (req, res) => {
  res.json({ message: 'Async CORS configuration!' })
})

app.listen(8080)

五、完整配置选项说明

cors 中间件支持丰富的配置项,覆盖所有 CORS 响应头需求:

配置项 类型 作用 示例
origin String/RegExp/Array/Function 配置允许的跨域来源 origin: 'http://example.com'
methods String/Array 配置允许的请求方法 methods: ['GET', 'POST', 'PUT']
allowedHeaders String/Array 配置允许的请求头 allowedHeaders: ['Content-Type', 'Authorization']
exposedHeaders String/Array 配置前端可访问的响应头 exposedHeaders: ['X-Total-Count']
credentials Boolean 是否允许携带 Cookie credentials: true
maxAge Number 预检请求的缓存时间(秒) maxAge: 86400(缓存 24 小时)
preflightContinue Boolean 是否将预检请求传递给下一个中间件 preflightContinue: false
optionsSuccessStatus Number 预检请求成功的状态码 optionsSuccessStatus: 200

如果前端需要在跨域请求中携带 Cookie(如身份认证),需同时配置前后端:

  • 后端配置

    javascript 复制代码
    var corsOptions = {
      origin: 'http://example.com',
      credentials: true // 允许跨域携带 Cookie
    }
    app.use(cors(corsOptions))
  • 前端配置(Axios 示例)

    javascript 复制代码
    axios.get('http://localhost:8080/api/data', {
      withCredentials: true // 携带 Cookie
    })

六、常见问题与解决方案

  1. 问题 :配置后仍然跨域报错
    原因 :可能是复杂请求未处理 OPTIONS 预检,或 origin 配置错误。
    解决方案 :添加 app.options('*', cors()),检查白名单是否包含前端真实域名。

  2. 问题 :IE11 浏览器跨域请求失败
    原因 :IE11 不兼容 204 状态码。
    解决方案 :配置 optionsSuccessStatus: 200

  3. 问题 :携带 Cookie 时跨域失败
    原因credentials 未开启,或 origin 配置为 *(不能同时使用 *credentials: true)。
    解决方案origin 配置为具体域名,同时开启 credentials: true

七、总结

cors 中间件是 Express 处理跨域的高效工具,核心配置思路为:

  • 开发环境 :使用 app.use(cors()) 允许所有来源。
  • 生产环境:配置白名单限制来源,处理复杂请求的预检,必要时开启 Cookie 支持。

合理配置 CORS 既能保证前后端通信正常,又能提升接口的安全性

相关推荐
水冗水孚10 小时前
告别黑盒!手写Windows版简易NodeMON,学习文件监听代码修改与进程服务重启知识
node.js·express
我的golang之路果然有问题13 小时前
实习中遇到的 CORS 同源策略自己的理解分析
前端·javascript·vue·reactjs·同源策略·cors
天意pt1 天前
Blog-SSR 系统操作手册(v1.0.0)
前端·vue.js·redis·mysql·docker·node.js·express
Benny的老巢3 天前
Cloudflare Workers CORS 跨域问题排查与解决
跨域·cloudflare·cors·workers
漫游嵌入式4 天前
《PCI EXPRESS体系结构导读》---(5)PCI总线Device号的分配
express·pcie·pci
bl4ckpe4ch4 天前
用可复现实验直观理解 CORS 与 CSRF 的区别与联系
前端·web安全·网络安全·csrf·cors
漫游嵌入式5 天前
《PCI EXPRESS体系结构导读》---(4)PCI总线Bus号初始化
express·pcie·pci
HWL56796 天前
Express图片上传功能,包括数据库存储
express
小新1107 天前
后台nodejs+express从sql server中获取数据
express·mssql
漫游嵌入式9 天前
《PCI EXPRESS体系结构导读》---(1)基本概念
express·pcie·pci