一个案例教你使用“跨域资源共享”

跨域资源共享(CORS),是一种解决跨域问题的解决方案。它的实现需要浏览器、服务器同时支持。又因为现代浏览器都已经支持了这个机制,所以CORS的实现关键在于服务器。

我们将根据"发送请求前,是否会发送预检请求"这一特征来将请求分为2类,一类是简单请求,一类是非简单请求。

下面我们将逐个进行讲解。

简单请求

简单请求,直接就将请求发送给服务器。简单请求具有以下特征:

  • 请求方式必须是 GETHEADPOST 这里面的其中一个。
  • Content-Type字段的值必须是 text/plainmultipart/form-dataapplication/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;另一类是自己实现。

我们这里选择自己实现。代码如下:

javascript 复制代码
router.get('/req1', function(req, res, next) {
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.send('响应成功-1');
});

这个时候我们再来看一下效果:

什么场景下,会用到简单请求呢?从业务上来看,跟用户没关系的功能一般都是简单请求。一般都是GET请求去获取一些整体资源啥的。

非简单请求

发送真正的请求前,会自动发送一个预检请求,这个预检请求是用来询问服务器,是否支持CORS。

除了简单请求,剩下的都是非简单请求。

服务端如何支持?

想发送一个非简单请求还是比较简单的,主要体现在额外的请求头字段、Content-Type的值为其他类型即可。

前端可能会这么来写:

javascript 复制代码
let result = await axios.create({
    baseURL: 'http://127.0.0.1:8888/',
    headers: {
        user_token: '123-qwe-789-gpto901'
    }
})
.post(
    '/cors/req2',
    {
        name: '章三',
    }
);

后端可能会这么写:

javascript 复制代码
router.post('/req2', function(req, res, next) {
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.send('响应成功-1');
});

如果此时发送请求,那它一定会有跨域问题。

这个报错信息也是很全面的,如何改正,它已经暗示我们了:

请求头"Access-Control-Allow-Origin"字段没有被预设在对应的请求资源上。

最开始的时候,就有说过,非简单请求在真正发送请求前,会发送一个options的预检请求。

所以我们需要单独给这个接口加上options方法的处理,处理如下:

javascript 复制代码
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"字段,后端接口修改如下:

javascript 复制代码
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('');
});

此时再次点击按钮,发现还是报错,信息如下:

说我们响应头字段里还少东西,那就接着添加呗:

javascript 复制代码
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。

现在来动手实践一下。

前端做出修改如下:

javascript 复制代码
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: '章三',
    }
);

后端修改如下:

javascript 复制代码
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 字段,这个字段用来判断 资源是否缓存失效的一个指标,后端做出修改如下:

javascript 复制代码
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字段就能够获取到了。

最后

好啦,本期分享到这里就结束啦,希望我的分享能够对你有帮助,我们下期再见啦,拜拜~~

关注公众号:熬夜学前端,获取更多干货实践,欢迎交流分享。

相关推荐
用户479492835691514 分钟前
JavaScript 的 NaN !== NaN 之谜:从 CPU 指令到 IEEE 754 标准的完整解密
前端·javascript
群联云防护小杜18 分钟前
国产化环境下 Web 应用如何满足等保 2.0?从 Nginx 配置到 AI 防护实战
运维·前端·nginx
醉方休1 小时前
Web3.js 全面解析
前端·javascript·electron
java1234_小锋1 小时前
Spring事件监听的核心机制是什么?
java·spring·面试
前端开发爱好者1 小时前
前端新玩具:Vike 发布!
前端·javascript
今天也是爱大大的一天吖1 小时前
vue2中的.native修饰符和$listeners组件属性
前端·javascript·vue.js
fxshy1 小时前
在 Vue 3 + Vite 项目中使用 Less 实现自适应布局:VW 和 VH 的应用
前端·javascript·less
奇舞精选2 小时前
AI时代的前端知识拾遗:前端事件循环机制详解(基于 WHATWG 最新规范)
前端·javascript
小月鸭2 小时前
理解预处理器(Sass/Less)
前端
AI3D_WebEngineer2 小时前
企业级业务平台项目设计、架构、业务全解之组件库篇
前端·javascript·vue