跨域资源共享(CORS),是一种解决跨域问题的解决方案
。它的完成需求浏览器、服务器同时支撑。又由于现代浏览器都已经支撑了这个机制,所以CORS的完成关键在于服务器。
咱们将根据“发送恳求前,是否会发送预检恳求”
这一特征来将恳求分为2类,一类是简略恳求,一类是非简略恳求。
下面咱们将逐个进行解说。
简略恳求
简略恳求,直接就将恳求发送给服务器。简略恳求具有以下特征:
- 恳求方式必须是
GET
、HEAD
、POST
这儿面的其中一个。 - Content-Type字段的值必须是
text/plain
、multipart/form-data
、application/x-www-form-urlencoded
这儿面的其中一个。 - 答应你新增恳求头字段,可是新增的恳求头字段 必须是下面集合里的某一个。
1、Accept
2、Accept-Language
3、Content-Language
4、Content-Type
5、Range
服务端怎么支撑?
特别提醒:
上方的图片便是未支撑CORS而爆出来的过错,过错信息很详细,咱们在工作中,根据过错提示一步一步修正即可。
针对简略恳求,想要让服务端支撑CORS,2种方式,分别如下:
- 将
Access-Control-Allow-Origin
字段的值设置为 “*”。 - 将
Access-Control-Allow-Origin
字段的值设置为“非*”。
这2种方式的差异在于恳求是否能够带着cookie。
第一种方式,想要带着用户信息,只能经过新增恳求头字段的方式来带着。
第二种方式,想要带着用户信息,除了能够运用第一种方式,还能够运用cookie。
在这篇文章里,咱们运用express来搭建服务器,express想要支撑CORS,2种手段,一类是运用第三方库,比方cors
;另一类是自己完成。
咱们这儿选择自己完成。代码如下:
router.get('/req1', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.send('呼应成功-1');
});
这个时分咱们再来看一下效果:
什么场景下,会用到简略恳求呢?从业务上来看,跟用户没关系的功能一般都是简略恳求。一般都是GET恳求去获取一些全体资源啥的。
非简略恳求
发送真实的恳求前,会主动发送一个预检恳求,这个预检恳求是用来询问服务器,是否支撑CORS。
除了简略恳求,剩余的都是非简略恳求。
服务端怎么支撑?
想发送一个非简略恳求仍是比较简略的,首要体现在额定的恳求头字段、Content-Type的值为其他类型即可。
前端可能会这么来写:
let result = await axios.create({
baseURL: 'http://127.0.0.1:8888/',
headers: {
user_token: '123-qwe-789-gpto901'
}
})
.post(
'/cors/req2',
{
name: '章三',
}
);
后端可能会这么写:
router.post('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.send('呼应成功-1');
});
假设此刻发送恳求,那它一定会有跨域问题。
这个报错信息也是很全面的,怎么改正,它已经暗示咱们了:
恳求头“Access-Control-Allow-Origin”字段没有被预设在对应的恳求资源上。
最开始的时分,就有说过,非简略恳求在真实发送恳求前,会发送一个options的预检恳求。
所以咱们需求独自给这个接口加上options方法的处理,处理如下:
router.post('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.send('呼应成功-1');
});
router.options('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.send('');
});
此刻再发送以下恳求,接口仍是报错,报错信息如下:
那就再接着修正呗,这次提示咱们说要在“Access-Control-Allow-Headers”里增加“content-type”字段,后端接口修正如下:
router.post('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "content-type");
res.send('呼应成功-1');
});
router.options('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "content-type");
res.send('');
});
此刻再次点击按钮,发现仍是报错,信息如下:
说咱们呼应头字段里还少东西,那就接着增加呗:
router.post('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "user_token,content-type");
res.send('呼应成功-1');
});
router.options('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "user_token,content-type");
res.send('');
});
此刻再次点击按钮,这时咱们发现,这个非简略恳求的CORS终于成功了。
总结如下:
想要让服务器支撑非简略恳求的CORS,需求做出如下设置:
- options的恳求、真实的恳求都需求设置 “Access-Control-Allow-Origin”呼应头字段。
- options的恳求、真实的恳求都需求设置 “Access-Control-Allow-Headers”呼应头字段。
CORS过程中,恳求怎么带着cookie?
假设咱们有这样的一个cookie:
咱们想要去带着Cookie,需求做出如下修正:
- 前端需求将 withCredentials 需求设置为true。
- 后端需求将 “Access-Control-Allow-Origin” 设置为非*号。
- 后端还需求将 “Access-Control-Allow-Credentials”设置为true。
现在来动手实践一下。
前端做出修正如下:
let result = await axios.create({
baseURL: 'http://127.0.0.1:8888/',
headers: {
user_token: '123-qwe-789-gpto901'
},
withCredentials: true
})
.post(
'/cors/req2',
{
name: '章三',
}
);
后端修正如下:
router.post('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:3000");
res.setHeader("Access-Control-Allow-Headers", "user_token,content-type");
res.setHeader("Access-Control-Allow-Credentials", true);
res.send('呼应成功-1');
});
router.options('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:3000");
res.setHeader("Access-Control-Allow-Headers", "user_token,content-type");
res.setHeader("Access-Control-Allow-Credentials", true);
res.send('');
});
点击发送恳求后,咱们发现,恳求主动带着了cookie。
CORS过程中,怎么设置OPTIONS预检恳求的缓存时长?
这个就直接说结论了,篇幅有些过长,就不跟着写代码了,想要实践的小伙伴能够自行实践。
设置 Access-Control-Max-Age
字段就能够了,这个字段的单位是 “秒”。
CORS过程中,前端怎么拿到其他的恳求头字段?
不知道咱们有没有重视过一个现象,咱们一般都会用axios来进行通信。可是,在呼应拦截器里,咱们会发现,这儿的头字段并不总是全的,如下:
咱们真实的呼应头字段却有很多个,如下:
针对这一个现象,假设前端想要获取到额定的呼应头字段,后端就需求设置Access-Control-Expose-Headers呼应头字段
。
就比方,前端想要在拦截器里获取 Etag
字段,这个字段用来判别 资源是否缓存失效的一个指标,后端做出修正如下:
router.post('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:3000");
res.setHeader("Access-Control-Allow-Headers", "user_token,content-type");
res.setHeader("Access-Control-Allow-Credentials", true);
res.setHeader("Access-Control-Expose-Headers", "X-Powered-By,Etag");
res.send('呼应成功-1');
});
router.options('/req2', function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:3000");
res.setHeader("Access-Control-Allow-Headers", "user_token,content-type");
res.setHeader("Access-Control-Allow-Credentials", true);
res.setHeader("Access-Control-Expose-Headers", "X-Powered-By,Etag");
res.send('');
});
此刻咱们再console一下呼应拦截器,咱们会发现,Etag字段就能够获取到了。
最终
好啦,本期共享到这儿就结束啦,期望我的共享能够对你有帮助,咱们下期再见啦,拜拜~~
重视大众号:熬夜学前端,获取更多干货实践,欢迎沟通共享。