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 既能保证前后端通信正常,又能提升接口的安全性

相关推荐
你想考研啊2 天前
win11卸载sql server express版本
express
克里斯蒂亚诺更新4 天前
vue展示node express调用python解析tdms
服务器·python·express
小天源9 天前
Oracle Database 11g Express Edition (XE) 11.2.0.2 在离线银河麒麟 V10 上的部署手册
数据库·oracle·express·麒麟v10·oracle11g·oracle-xe-11g
C++实习生14 天前
Visual Studio Express 2015 for Windows Desktop 中文学习版
windows·express·visual studio
C++实习生14 天前
Visual C++ 2005 Express 中文版
express·c++20
张彦峰ZYF14 天前
QLExpress 字符串能力解析:机制、用法与工程实践
字符串·express·qlexpress规则表达力
biyezuopinvip14 天前
基于uni-app和Express的问答对战小程序的设计与实现(论文)
小程序·uni-app·毕业设计·论文·express·毕业论文·问答对战小程序的设计与实现
天意pt15 天前
Idempotency 幂等性 - 点赞和投票功能
前端·javascript·express
Java程序员-小白16 天前
Sa-Token过滤器引发的CORS误判问题
vue.js·elementui·axios·cors
水冗水孚21 天前
告别黑盒!手写Windows版简易NodeMON,学习文件监听代码修改与进程服务重启知识
node.js·express