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

跨域资源共享(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;另一类是自己实现。

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

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字段就能够获取到了。

最后

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

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

相关推荐
小狼154548 分钟前
浏览器扩展脚本为什么有时候不生效:注入时机、iframe 和单页路由,多多开票助手
前端·chrome
じòぴé南冸じょうげん22 分钟前
油猴脚本突然发现变成灰色了,无法使用?页面不生效?刷新页面没反应?
前端
怕浪猫1 小时前
Prompt Engineering 面试怎么考?这 5 个范式你必须会
面试·程序员·github
Csvn2 小时前
组合式 API(Composition API)
前端
是翎3 小时前
AI开发工程师面试指南
人工智能·面试·职场和发展
火柴就是我3 小时前
Android 打包报错 25.0.3
android·前端
BD_Marathon3 小时前
消息对象中字段的说明
java·前端·python
八荒启·交互动画3 小时前
Web特效025—用 Canvas 2D 做Web特效的定义与边界:这支画笔能做到哪一步
前端·webgl·网页特效·八荒启-交互动画·八荒启
Ai-_Man3 小时前
您您这可以把Dola的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。用AI导出鸭,答案是可以的
开发语言·前端·人工智能·小程序
excel4 小时前
Nuxt 中使用 useHead 优化 SEO 与 GEO
前端