http cors 简介

跨域问题

首先启动2个server,一个代理前端的代码,一个提供api接口
这里前端我偷懒使用http-server这个模块,
前端写一个简单的ajax请求后端的接口,

前端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
fetch('http://localhost:3000/')
.then(res => res.json())
.then(data => {
console.log(data)
})
.then((err) => {
console.log(`error: ${err}`);
})
</script>
</body>
</html>

后端代码

1
2
3
4
5
6
7
8
9
10
let koa = require('koa');
let app =new koa();

app.use(async (ctx, next) => {
ctx.body = {code: 0, data: `successful`};
});

app.listen(3000, () => {
console.log(`listen 3000 ...`);
})

准备好实验环境,分别启动对应的server,用浏览器访问结果如下:

这是比较经常遇到的跨域问题,解决办法1. 代理 2. 后端处理,为了解决这个问题,修改后端的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
let koa = require('koa');
let app =new koa();

app.use(async (ctx, next) => {
ctx.set({
"Access-Control-Allow-Origin": "*"
});
ctx.body = {code: 0, data: `successful`};
});

app.listen(3000, () => {
console.log(`listen 3000 ...`);
})

再起重启服务器,刷新浏览器

如果把get请求改成post,并且在header里加上个token,结果会怎么样,试一下

修改前端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
fetch('http://localhost:3000/', {
method: 'POST',
headers: {
'token': '123'
}
})
.then(res => res.json())
.then(data => {
console.log(data)
})
.then((err) => {
console.log(`error: ${err}`);
})
</script>

访问浏览器

是不是很奇怪,平时我们开发vue等单页应用经常用axios在header里面添加token没报过这个错,
为什么这里会报错呢

原因:vue使用了webpack里面的devserver-proxy,帮助我们开发的时候后端代理了,如果在2个不同的域名下就会这样。

所以,在一些浏览器允许的字段白名单之外,都是被浏览器视为不安全的,就算设置了跨域,没设置跨域的heder也是会报错的,解决办法,就是在后端添加进去我们要传的头。

修改后端代码

1
2
3
4
5
app.use(async (ctx, next) => {
ctx.set("Access-Control-Allow-Origin","*");
ctx.set("Access-Control-Allow-Headers", "token");
ctx.body = {code: 0, data: `successful`};
});

再重启服务器,然后刷新页面

再服务器设置了

1
ctx.set("Access-Control-Allow-Origin","*");

之后,并不是万能的,很多浏览器不允许的字段还是不行的,再浏览器预请求会报错。

浏览器允许的字段有下面这些,具体参照mdn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
------------
GET
HEAD
POST
------------
Accept
Accept-Language
Content-Language
Content-Type (需要注意额外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width
------------
Content-Type 的值仅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded

不在这里面的都会进行预请求。

参考第三方库,里面一般都包含这几个

1
2
3
4
5
6
7
8
9
10
ctx.set("Access-Control-Allow-Origin", "*");
// 允许所有的域名
ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE");
// 允许的方法
ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type");
// 支持的头信息
ctx.set("Access-Control-Allow-Credentials", true);
// 跨域携带cookie,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*"
ctx.set("Access-Control-Max-Age", 300);
// 设置300s内不用再次进行预请求也就是再发option请求