这是我参与「第四届青训营 」笔记创作活动的第4天
HTTP——篇③
写在前面
(1)文章摘要
- 浏览器的同源策略
- 跨域资源共享
- 服务器什么时候会返回数据?
- “简单请求”和预检请求
(2)读前须知
一、跨域问题引入?
- 你是不是经常遇到这样的情况?
二、几个思考
- 其实这就是一个常见的跨域错误。
has been blocked by CORS。可是我们可以思考一下。 - 什么是跨域呢?
- 为什么会跨域呢?
- 是谁阻止了跨域请求呢?
三、大胆尝试
- 在解决这些问题之前,我们先来试着发两个请求。
(1)Form表单请求【一切正常】
- 图一
- 浏览器不报错,服务器正常返回数据
(2)Ajax请求【跨域错误,简单请求】
- 图二
- 浏览器报错,服务器正常返回数据
(3)Ajax请求【跨域错误,预检请求】
- 图三
- 浏览器报错,服务器也没有返回数据。
①新的思考
-
看完了这张图片,你是否又有新的问题了呢?
-
请求同一个接口。
-
为什么使用表单请求,浏览器的控制台就没有报跨域错误?
-
为什么使用Ajax请求,浏览器的控制台出现了跨域错误?
-
预检请求和简单请求的区别是什么呢?
-
服务器什么时候会返回数据呢?
-
②什么是简单请求?
-
不会触发预检请求的请求,在MDN上称这样的请求为预检请求。简单请求满足特定的条件。如
-
使用
GET、HEAD、POST请求方法之一 -
请求头字段,是不怎么规范的字段。(这里指的是,不怎么符合Fetch规范)
-
具体的可以看看MDN对它的定义:简单请求
③什么是预检请求?
- 非简单请求。预检请求会先用
OPTIONS方法,发一个请求给服务器 - 去询问服务器是否允许该客户端发起实际的请求。
四、问题解答
(1)什么是跨域请求?
- 带着上面的诸多疑问,我们先来看一下,跨域资源共享的定义
跨域资源共享(英语:Cross-origin resource sharing,缩写:CORS),用于让网页的受限资源能够被其他域名]的页面访问的一种机制。通过该机制,页面能够自由地使用不同源的图片、样式、脚本、iframes以及视频。跨源共享一些跨域的请求(特别是Ajax)常常会被同源策略(Same-origin policy)所禁止。跨源资源共享定义了一种方式,为的是浏览器和服务器之间能互相确认是否足够安全以至于能使用跨源请求(cross-origin requests)。跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同网域传来脚本的方法,以避开浏览器的同源策略。
- 以前 前后端未分离的时候
- 请求的资源几乎都是放在项目同源的目录下。几乎不会有跨域问题
- 随着Ajax的出现,前后端慢慢分离了。
- 我们不得不去发起异步请求,从不同域、不同源头获取资源。有时候就会出现跨域的问题。
- 我们想要跨域共享资源,必须得遵守一些规范。
(2)为什么会跨域?是谁造成了跨域?
- 请求一些资源时,会受浏览器同源策略的影响。
- 为了安全起见,浏览器出现了同源策略。
- 换句话说,浏览器认为你的行为安全,那么就让你通过
- 可以说,是浏览器的同源策略造成了跨域。
①什么是同源策略?
-
简单来说,同源就是:URL的
协议 + 主机 + 端口一样,既为同源 -
MDN上面的表格
- 同源策略是用来控制不同源之间的交互。
- 它规定了:默认情况下,AJAX请求只能发送给同源的URL。
- 有一些资源不受同源策略的影响
- img、script、link、iframe、video、audio等标签,不受同源策略的约束。
(3)表单、Ajax请求的细节
①Form表单为什么没有跨域问题?
- 我们刚刚说了,会不会产生跨域问题,完全是看浏览器
- 浏览器认为你安全,那么你就安全
- 它觉得你不安全,就将你拒之门外
- 那么为什么浏览器认为Form表单是安全的呢?
- Form表单在请求后,URL会立即跳转到新请求的接口
- 接下来的逻辑,一般交由后端处理
- 无须返回数据给原先发起请求的页面
- 所以浏览器认为这是一个安全的行为,即没必要受到同源策略的保护
②Ajax请求为什么会跨域
-
Ajax为什么要受到同源策略的保护
-
Ajax请求后,后端负责给出数据,
-
接下来的逻辑,一般交由前端处理
-
在原先请求的页面,用户可以通过某些手段,拿到请求后的数据。
-
所以浏览器认为这是一个不安全的行为,即不直接让资源可以跨域共享。
-
③服务器什么情况会返回数据呢?
-
我们刚刚提到了,同源策略是浏览器的策略,和服务器可没多大关系
-
服务器为何时而返回数据,时而不返回数据。
-
你可能也在想,浏览器控制台都报了跨域错误,为什么服务器有时还能给你返回数据呢?
-
直接先看流程图
- 看完这张图之后,你应该就了解了,服务器什么时候会返回数据。
- 那我们再来看几个细节
1、简单请求的细节
- 如果符合简单请求,那么浏览器什么也不用考虑
- 直接发送真实的请求给服务器
- 排除其他异常情况,服务器也不管什么
- 直接返回数据给浏览器
- 浏览器拿到数据之后,查看响应报文
- 看服务器是否支持请求所在的源。如果不满足,那么把数据屏蔽掉。抛出跨域的异常错误。
- 如果满足,正常响应。
2、预检请求的细节
- 这时候,预检请求就要派上用场了。
- 浏览器为了减小开销。在发送非简单请求时。
- 会先发送一个预检请求去询问服务器。去询问它
- 你允许什么方法?允许什么头部字段?
- 最重要的是,询问它,请求所在的源
【Origin】是否支持。 - 浏览器收到响应的消息过后。
- 就可以确定,是否使用同源策略来阻止真正的请求发出。
- 浏览器查看自己的域,服务器不支持接收,那么就不发送真正的请求给服务器了(发了也是浪费开销,反正浏览器也会将数据屏蔽掉)
- 如下【发预检请求,发现不支持我这个域,那么就不会发送真实的请求】
- 只有一个预检请求
OPTIONS /users/login HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Accept: */*
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Access-Control-Request-Private-Network: true
Origin: http://192.168.1.8:8081
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Sec-Fetch-Dest: empty
Referer: http://192.168.1.8:8081/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
- 浏览器查看自己的域,服务器支持接收,那么就发送真正的请求给服务器。
- 如下【发送预检请求,发现服务器支持我这个源,接下来就发送真正的请求】
- 前后一共发了两个请求【预检请求 + 真实请求】
五、相关字段
(1)Origin
- 发起一个针对跨域资源共享的请求
- 示例
Origin: http://192.168.1.8:8081
(2)Access-Control-Allow-Origin
-
指定哪些网站可参与到跨来源资源共享过程中
-
示例
Access-Control-Allow-Origin: http://localhost:8081
写在后面
(1)参考文献
(2)读后思考
-
读过本文,可以去思考一下,下面解决跨域的常见方案。
-
为什么可以解决?
-
具体怎么实现?
- 前端使用代理可以解决跨域问题
- 后端可以设置对应响应头解决跨域问题