上一节介绍了前端网络的基础用法,已经秒杀了fetch与xhr用法,但是实际在前端发送这些请求的时候,难免会遇到一些莫名其妙的报错,在别人网站正常请求的服务器地址,在你的网站里面就不行了,我用APIfox,postman或者vscode的rest client发送请求就一切正常,这是怎么回事?
其实这是来自于浏览器的安全策略“跨源资源共享”
跨源资源共享(CORS,或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。
例如a.com发送一个POST请求到服务器,是正常的,而b.com发送却失败,这就是因为服务器有着一个“Access-Control-Allow-Origin”响应头,检测到b.com不在允许请求的范围内,故浏览器拦截了这一请求。但是有个例外,上述提到的APIFox,postman这些工具能正常请求的原因是他是从服务端进行请求的,不是浏览器的环境,自然没有这样的限制,你可以理解为浏览器是高中的学生,他不允许你使用手机,而postman是高中老师,自然不受校规对学生用手机的限制。
以下情况,都会引起跨源的情况
检查两个域之间是否产生跨源问题,可以前往 https://httptoolkit.com/will-it-cors/ 进行测试
本节重点关注http请求的部分,在http中,浏览器将 CORS 请求分成两类:简单请求(Simple request)和非简单请求(Not-so-simple request)
如果以下三种类型都满足,则视为简单请求的类型
满足简单请求的时候,浏览器会向服务器原封不
动地发送请求
浏览器封装后的http请求类似下文
GET /release/ HTTP/1.1
Host: api-gz.arsrna.cn
accept: */*
accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
dnt: 1
origin: https://www.arsrna.cn
referer: https://www.arsrna.cn/
sec-ch-ua: "Microsoft Edge";v="123", "Not:A-Brand";v="8", "Chromium";v="123"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0
随后获取服务器返回的Access-Control-Allow-Origin
响应头,确定是否进行跨源异常处理,例如上面的请求,服务器返回了如下请求头:
可以看到,Access-Control-Allow-Origin
为*
,说明允许所有域名跨源请求这个API。
浏览器看到了觉得没问题,那就通过。
如果简单请求条件的其中一条不满足,则视为非简单请求。
在进行非简单请求时,浏览器会按顺序一步步执行,比如这里我们做了一个GET请求,并且携带了一个自定义的Token
请求头
由于Token
请求头超出了简单请求的范围,浏览器会进行下面的一系列操作。
OPTIONS
的请求进行预检,在浏览器开发者工具网络面板里面可以看到Access-Control-Allow-Headers
,Access-Control-Allow-Methods
,Access-Control-Allow-Origin
响应内,则浏览器准许放行。这样的请求就类似小区门口的保安,如果车牌,车主和手机号(请求头,方法,域)都登记在小区(服务器)内,保安检查(预检)觉得没问题,就准许放行,此时我们就可以正式访问,进到小区里面。
整套流程下来发现,其实是浏览器限制了我们这么操作,但是归根结底不是前端的问题,实际上CORS是服务器的锅。在上一节提到了,客户端和服务器发送请求是双方协商好的,不是因为前端有GET后端才能GET,也不是因为后端有Accept-Content
这个头,我就拿这个头传数据。
前端请求,也只是遵循了写后端的程序员写的规则而已
后端返回,也只是听从了前端程序员的建议而已
他俩其实谁也不怪谁,如果真正发生CORS的错误,需要后端改动响应头的内容。而前端能做到的,就是什么也不做,因为问题的根源在浏览器本身,你当然可以通过修改浏览器配置使其不再检测,但是成千上万的用户,谁也不知道谁有没有这个限制。
既然浏览器和前端没法变通,那么后端的人员就得精明一点了,老老实实按照规范把这些响应头加回去。
虽说这里讲的是前端网络,不过可以稍微点一下后端的内容
因为后端百花齐放,千奇百怪,所有的语言,框架从a-Z,0-9可以给你列出几千万种。
这里就以nodejs的express为例,讲一下如何解决前端跟你提的CORS的问题
既然我们知道了这个是由响应头不对劲引起的,那么就可以再每次请求的时候给浏览器一个正确的回复
注意:以下是错误写法
app.get('/test',(req,res)=>{
res.header("Access-Control-Allow-Origin","*");
})
如果在简单请求可能还不会出事,但是如果是非简单请求,打印网络日志,会发现浏览器先发送了OPTIONS请求后再发送GET请求,由于这里写的是app.get
,意味着只有在GET
请求的时候才会正确处理发送响应头的函数,所以OPTIONS就已经被拦截了,不会正式发送GET
请求。
正确写法如下:
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
next();
});
对所有请求都做如上返回响应头的处理,这样能保证浏览器预检能够通过。
其实这不是你的问题,是浏览器限制和后端不正确的响应共同导致的。对于前端来说只能提供一个思路,告诉前端人员这个错误造成的原因,要真修起来只能干瞪眼。前端能做的就是鞭策后端工程师赶紧修了,你的责任就是等他修好。
到这里,关于在前端进行网络请求的内容就已经算是入门+1了,往后仍有更长的路要走,本章仅仅对HTTP的CORS进行讲解,以后还会遇到像上传文件,下载文件,跑通接口,跨域,认证,jwt token,session,登录注册,SSE(服务器主动发送事件),Websocket(服务器客户端双向通信)等等一系列更复杂的实战挑战,一切的前提,是学会HTTP,解决CORS的问题,成功在前端跑通服务器。(来自上一篇文章)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。