CORS 跨域资源共享的实现
一、原生 Node.js 实现 CORS
1、实现方式
通过设置 Access-Control-Allow-Origin 响应头来实现 CORS 跨域资源共享:
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Access-Control-Allow-Origin', '*');
// * 代表所有域,如果设置指定域,可通过以下方式
// res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8001');
res.end('Hello World\n');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
2、应用实例
继前面 Session 做登录验证的代码,点击查看完整前后端代码,将 front-end-project/src/http/config/index.ts
下的请求基础地址与 Server 同步:
const serverConfig = {
// baseURL,
baseURL: 'http://localhost:7676',
useTokenAuthorization: true // 是否开启 token 认证
}
运行:
# 任意目录 启动 redis
redis-server
# server 项目下启动 server
yarn dev
# 前端项目下启动静态页面项目
yarn start
静态项目页面运行在 8001
环境:
服务运行在 7676
环境:
前端 8001
环境调用服务端 7676
环境的接口时,会出现跨域的报错:
通过上面的方式实现 CORS 如下:
const serverHandle = (req, res) => {
// ...
res.setHeader('Access-Control-Allow-Origin', '*')
// ...
}
到这里,在前端 8001
环境可以成功访问到后端 7676
环境的 GET 请求,但 POST 请求会出现以下错误:
这是由于前端发起了复杂请求,触发了 CORS 的预检请求(Preflighted Requests),通过 CORS 解决跨域问题会在发送请求时出现两种情况:简单请求、复杂请求。
3、简单请求与复杂请求
3-1、简单请求
同时满足以下两大条件就属于简单请求。
条件1:使用以下方法之一:
- GET
- POST
- HEAD
条件2:请求的 Header 是:
- Accept
- Accept-Language
- Content-Language
- Content-Type: 只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain
对于简单请求,直接设置 Access-Control-Allow-Origin 响应头即可实现 CORS。
3-2、复杂请求
不符合以上条件的请求就是复杂请求。
复杂请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为预检请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。解决方式:
const serverHandle = (req, res) => {
// ...
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
return res.end();
}
// ...
}
默认情况下,跨源请求不提供凭据(cookie、HTTP 认证及客户端 SSL 证明等),如果请求需要使用到 cookie,可以进行以下设置 ▼
在前端请求时设置 withCredentials 为 true
:
const serviceAxios = axios.create({
baseURL: serverConfig.baseURL, // 基础请求地址
timeout: 10000, // 请求超时设置
withCredentials: false, // 跨域请求是否需要携带 cookie
withCredentials: true, // 跨域请求是否需要携带 cookie
});
注意前端请求 withCredentials 设为 true
后,Server 的 Access-Control-Allow-Origin 就不能写 "*"
了,需要指定具体域,然后设置 Access-Control-Allow-Credentials 允许后端跨域传递 Cookie:
const serverHandle = (req, res) => {
// ...
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8001');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader("Access-Control-Allow-Credentials", true); // 允许后端跨域传递 Cookie
if (req.method === 'OPTIONS') {
return res.end();
}
// ...
}
到这里,就可以在前端 8001
环境正常访问后端 7676
环境的接口了。点击查看完整代码
二、Express 使用中间件实现
使用 Express 框架时,可以直接使用 cors 中间件实现跨域,安装如下:
npm install cors
使用如下:
// ...
const cors = require('cors');
// CORS 跨域
app.use(cors({
origin: 'http://localhost:8001',
allowedHeaders: 'Content-Type',
credentials: true
}))
三、Koa 使用中间件实现
Koa 可以使用 @koa/cors 中间件实现跨域,安装如下:
npm install @koa/cors --save
用法与 Express 一样:
// ...
const cors = require('@koa/cors');
// CORS 跨域
app.use(cors({
// 允许单个域名
origin: 'http://localhost:8001',
// 允许多个域名
// origin: (ctx) => {
// const allowCors = ['http://localhost:8001', 'http://localhost:8002'];
// return allowCors.indexOf(ctx.header.origin) > -1 ? ctx.header.origin : '';
// },
allowedHeaders: 'Content-Type',
credentials: true
}))