Node系列 · Express:cors 中间件

Node系列 · Express:cors 中间件

上一章讲了 CORS 原理------这一章用 cors 中间件把它从手动配置响应头简化成一行 app.use(cors())

一、安装与最简配置

bash 复制代码
npm install cors
javascript:basic-cors.js 复制代码
const cors = require('cors');

// 允许所有跨域(仅开发环境!)
app.use(cors());

这一行相当于手动加了:

javascript:equivalent-headers.js 复制代码
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, ...');
  next();
});

app.options('*', cors());  // 自动处理预检

二、生产配置

2.1 指定允许的 Origin

javascript:whitelist-cors.js 复制代码
const cors = require('cors');

const ALLOWED_ORIGINS = [
  'https://app.example.com',
  'https://admin.example.com',
];

app.use(cors({
  origin: (origin, callback) => {
    // origin 在同源请求时为 undefined(直接放行)
    if (!origin || ALLOWED_ORIGINS.includes(origin)) {
      return callback(null, true);
    }
    callback(new Error('CORS not allowed'));
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400,                  // 预检缓存 24 小时
}));

2.2 关键 options

选项 默认 说明
origin * 允许的源
methods GET, HEAD, PUT, PATCH, POST, DELETE 允许的方法
allowedHeaders Origin, X-Requested-With, ... 允许的请求头
exposedHeaders (空) 浏览器可读取的响应头
credentials false 是否允许 Cookie
maxAge 5(秒) 预检缓存时间

2.3 origin 三种写法

javascript:origin-forms.js 复制代码
// 1. 字符串(具体域名或 *)
cors({ origin: 'https://app.example.com' });
cors({ origin: '*' });

// 2. 数组
cors({ origin: ['https://a.com', 'https://b.com'] });

// 3. 函数(动态判断)
cors({
  origin: (origin, callback) => {
    if (WHITELIST.includes(origin)) {
      callback(null, true);     // 允许
    } else {
      callback(new Error('Not allowed'));
    }
  },
});
javascript:credentials-cors.js 复制代码
app.use(cors({
  origin: 'https://app.example.com',  // 不能是 *
  credentials: true,
}));

客户端配合:

javascript:client-credentials.js 复制代码
fetch('https://api.example.com/users', {
  credentials: 'include',         // 携带 Cookie
});

四、只对部分路由生效

不必全局 app.use(cors())------可以只对 API 路由生效:

javascript:partial-cors.js 复制代码
// 公共 API:允许所有跨域
app.use('/api/public', cors());

// 私有 API:只允许特定 Origin + 携带 Cookie
app.use('/api/private', cors({
  origin: 'https://app.example.com',
  credentials: true,
}));

// 不需要跨域的内部路由:不挂 cors
app.use('/api/internal', requireAuth);

五、自定义成功 / 失败响应

javascript:custom-response.js 复制代码
app.use(cors({
  origin: (origin, callback) => {
    if (!WHITELIST.includes(origin)) {
      return callback(new Error('CORS blocked'));
    }
    callback(null, true);
  },
}));

// CORS 失败的统一错误处理
app.use((err, req, res, next) => {
  if (err.message === 'CORS blocked') {
    return res.status(403).json({ error: 'CORS not allowed' });
  }
  next(err);
});

六、不同环境差异化配置

javascript:env-cors.js 复制代码
const isDev = process.env.NODE_ENV !== 'production';

app.use(cors(isDev
  ? {
      // 开发环境:宽松
      origin: true,                // 反射所有 Origin
      credentials: true,
    }
  : {
      // 生产环境:严格白名单
      origin: (origin, cb) => {
        if (PROD_ORIGINS.includes(origin)) {
          cb(null, true);
        } else {
          cb(new Error('CORS blocked'));
        }
      },
      credentials: true,
      maxAge: 86400,
    }
));

七、常见问题

7.1 cors() 不生效?

中间件顺序 问题------cors 必须在路由前注册:

javascript:order.js 复制代码
// ✅ 正确
app.use(cors());
app.use(express.json());
app.use('/api/users', userRoutes);

// ❌ 错误:路由先于 cors
app.use('/api/users', userRoutes);
app.use(cors());  // 太晚了

7.2 预检失败?

浏览器 OPTIONS 请求的响应没有 CORS 头:

bash 复制代码
$ curl -X OPTIONS https://api.example.com/users \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST"

# 看响应头里有没有:
# Access-Control-Allow-Origin
# Access-Control-Allow-Methods
# Access-Control-Allow-Headers

cors 中间件会自动处理 OPTIONS 预检响应------前提是配置正确。

检查三处:

位置 必须
服务端 credentials: true + 具体 Origin(不能 *
客户端 credentials: 'include'
浏览器 看到 Access-Control-Allow-Credentials: true 才带 Cookie

八、最佳实践

场景 推荐
开发环境 app.use(cors()) 一行搞定
生产环境 动态白名单 + credentials: true + maxAge: 86400
多环境 用环境变量控制 origin 列表
只对部分路由 局部 app.use('/api/xxx', cors(...))
安全 永远不要 在生产用 origin: '*'
缓存 多 Origin 配 Vary: Origin(cors 中间件自动加)

九、小结

  • cors 中间件把手动配响应头简化成 app.use(cors())
  • 生产环境用 origin: 函数 做白名单校验
  • 携带 Cookie 必须三件套:服务端 credentials: true + 具体 Origin + 客户端 credentials: 'include'
  • 中间件顺序:corsexpress.json → 业务路由
  • cors 自动处理 OPTIONS 预检,但配置错误仍会失败
  • 开发用宽松配置,生产用严格白名单
相关推荐
StevenSurpass1 小时前
智能工厂场景:FastPrintAgent 分布式打印中间件落地应用方案
分布式·mqtt·http·中间件·打印·fastreport
程序员小八7772 天前
Go Web 工程化:日志、配置与错误处理中间件,让服务「能上线」
前端·中间件·golang
tqs_123452 天前
SaaS 多租户 Agent 平台实战:自研 Agent 中间件 SDK,Nacos 配置驱动实现开箱即用
中间件
未若君雅裁2 天前
自定义中间件:Node-style 与 Wrap-style 钩子全解析
python·中间件·langchain
__zRainy__3 天前
Node系列 · Express:cookie 的基本概念
express
__zRainy__4 天前
Node系列 · Express:常用的中间件
中间件·node.js·express
FungLeo4 天前
成为全栈·Node 后端篇·框架选型:Express、Koa、Fastify、NestJS 的差异与为何选 Hono
express·koa·nest.js·fastify·hono
2601_962181964 天前
【ROS2 中间件RMW】基于FastDDS共享内存实现ROS2跨进程零拷贝通讯
中间件
未若君雅裁4 天前
上下文压缩双刃剑-Summarization与ContextEditing中间件实战
python·中间件·langchain