为何一个axios请求会发起两次(详解)?

一直都有这样的疑问,为何有的项目中明明这个请求我只写了一次,可是在控制台中会出现两遍,今天仔细看了下相关内容,作了以下记录:

问题:

以下图所示:在项目中只写了一次的请求,在实际netWork中发送了两次,第一次为不带参数的请求方式为options的请求,第二次为咱们本身定义的带了参数的请求方式javascript

1.png

2.png

1. 由于vue是没有提供ajax请求功能,因此须要使用vue-resource、axios等插件实现ajax请求

2. axios本质上是javascript的ajax封装,因此在使用axios发送请求时会被同源策略限制

什么是同源策略:

1995年,同源政策由 Netscape 公司引入浏览器。目前,全部浏览器都实行这个政策。html

最初,它的含义是指,A 网页设置的 Cookie,B 网页不能打开,除非这两个网页“同源”。所谓“同源”指的是”三个相同“。vue

即:协议相同 域名相同 端口相同

如:www.example.com/dir/page.ht…

协议是http://, 域名是 www.example.com ,端口是80(默认端口能够省略)java

目前,若是非同源,共有三种行为受到限制:(以下所示:)ios

(1) 没法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。web

(2) 没法接触非同源网页的 DOM。ajax

(3) 没法向非同源地址发送 AJAX 请求(能够发送,但浏览器会拒绝接受响应)axios

另外,经过 JavaScript 脚本能够拿到其余窗口的window对象。若是是非同源的网页,目前容许一个窗口能够接触其余网页的window对象的九个属性和四个方法。(自查)后端

同源策略的目的是为了保证用户信息的安全,防止恶意的网站窃取数据。

那么,当咱们发起http请求的时候,因为同源策略会致使跨域的问题,接下来就讲讲跨域:api

跨域场景有哪些呢:

3.png

关于上图,针对我本身不太理解的进行下解释:

4.png

关于这一条,举个例子,好比说如今有两个页面,一个是用域名访问的( http://www.domain.com ),
用这个域名寻址(DNS服务器)到的ip地址为192.168.4.12,另外一个页面是直接用这个ip地址访问的页面
(http://192.168.4.12 ),这两个页面任然不能通信
复制代码

5.png

不少小伙伴对域名可能也是只知其一;不知其二的,跟我同样,我找到了一张比较清晰的图,以下:

6.png

.com 顶级域名(一级域名)

zzvips.com 一级域名

www .zzvips .com 二级域名

bingo:简单点的记忆方法就是有几个点就是几级域名

跨域解决方案:

同源政策规定,AJAX 请求只能发给同源的网址,不然就报错。

除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制。

JSONP

WebSocket

CORS

JSONP:

JSONP 是服务器与客户端跨源通讯的经常使用方法。最大特色就是简单适用,老式浏览器所有支持,服务端改
造很是小。


它的基本思想是,网页经过添加一个<script>元素(script/link/img标签的属性不受同源政策限制),
向服务器请求 JSON 数据,这种作法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字
的回调函数里传回来。
复制代码

WebSocket

WebSocket 是一种通讯协议,使用ws://(非加密)和wss://(加密)做为协议前缀。该协议不实行同
源政策,只要服务器支持,就能够经过它进行跨源通讯。
复制代码

CORS

CORS 是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX
请求的根本解决方法。它容许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同
源使用的限制。相比 JSONP 只能发GET请求,CORS 容许任何类型的请求。
复制代码

CORS须要浏览器和服务器同时支持。目前,全部浏览器都支持该功能(IE浏览器不能低于IE10)。

整个CORS通讯过程,都是浏览器自动完成,不须要用户参与。对于开发者来讲,CORS通讯与同源的AJAX通讯没有差异,代码彻底同样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息有时还会多出一次附加的请求,但用户不会有感受。

所以,实现CORS通讯的关键是服务器。只要服务器实现了CORS接口,就能够跨源通讯。

看到这里的小伙伴应该就知道了咱们在为何一个请求会发起两次了,正是由于后端开启了cors跨域资源共享

CORS两种请求

浏览器将CORS请求分红两类:简单请求(simple request)和非简单请求(not-so-simple request)。

简单请求

若请求知足全部下述条件,则该请求可视为“简单请求”:

1. 使用下列方法之一:

GET

HEAD(与GET相似,可是HEAD并不返回消息体,响应能够被缓存,通常用于检查资源的有效性、检查超连接的有效性、 检查网页是否被串改、多用于自动搜索机器人获取网页的标志信息,获取rss种子信息,或者传递安全认证信息等)

POST

2. 除了被用户代理自动设置的首部字段(例如 Connection ,User-Agent)和在 Fetch 规范中定义为 禁用首部名称 的其余首部,容许人为设置的字段为 Fetch 规范定义的 对 CORS 安全的首部字段集合。该集合为:

Accept

Accept-Language

Content-Language

Content-Type (须要注意额外的限制)

DPR

Downlink

Save-Data

Viewport-Width

Width

其实总结就是:无自定义的header,而且HTTP头部信息不超过以上几种字段

3. Content-Type 的值仅限于下列三者之一:

text/plain

multipart/form-data

application/x-www-form-urlencoded

3. 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可使用 XMLHttpRequest.upload 属性访问。

4.请求中没有使用 ReadableStream 对象。

例如:

站点 foo.example 的网页应用想要访问 bar.other 的资源 那么浏览器请求头上就会带上Origin(Origin:协议+域名+端口号)这个字段,用于标识来源,服务器会根据这个值来决定是否容许其跨域。 若是服务器容许跨域,则须要在相应头中携带以下信息:

1. Access-Control-Allow-Origin:http: //foo.example(或是 * ) 

2. Access-Control-Allow-Credentials:true

3. Content-type:text/html; charset=utf-8
复制代码

Access-Control-Allow-Origin:容许哪一个域名进行跨域,是一个具体的域名或者 * ( * 表明任何域名)

Access-Control-Allow-Credentials:是否容许携带cookie,默认状况下CORS不会携带cookie,除非这个值是true

想要操做cookie须要知足3个条件:

  1. 服务器响应头中须要携带Access-Control-Allow-Credentials而且值为true

  2. 在浏览器发起ajax请求时须要在请求头上指定withCredentials:true

  3. 响应头中的Access-Control-Allow-Origin必定不能为*,必须是指定的地址

withCredentials:

是一个Boolean类型,它指示了是否该使用相似cookies,authorization headers(头部受权)或者TLS客户端证书这一类资格证书来建立一个跨站点访问控制(cross-site Access-Control)请求。在同一个站点下使用withCredentials属性是无效的。

这个指示也会被用作响应中cookies 被忽视的标示。默认值是false

若是在发送来自其余域的XMLHttpRequest请求以前,未设置withCredentials 为true,那么就不能为它本身的域设置cookie值。而经过设置withCredentials 为true得到的第三方cookies,将会依旧享受同源策略,所以不能被经过document.cookie或者从头部相应请求的脚本等访问。

实例:

var xhr = new XMLHttpRequest();

xhr.open('GET', 'http://example.com/', true);

xhr.withCredentials = true;

xhr.send(null);
复制代码

8.png

非简单请求

不符合一上条件的为复杂请求, 若是为复杂请求,则会发起一个请求方式为options的预请求,以下所示:

9.png 浏览器经过Origin字段先询问服务器当前网页所在的域名是否在服务器的许可名单内,以及可使用哪些Http动词和头信息字段

只有经过了预检请求浏览器才会发出正式的携带相关参数的XMLHttpRequest请求,不然就报错

一个“预检”请求样板:

OPTIONS  /cors HTTP/1.1
Origin:  http://liudan.handou.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.leyou.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
复制代码

与简单请求相比,除了Origin之外,多了两个头:

Access-Control-Request-Headers:额外会用到的请求头信息

Access-Control-Request-Method:请求方式 ,如PUT

预检请求的响应头:(服务器收到的预检请求后,若是容许跨域会发出响应):

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2021 01:15:39 GMT
Server: Apache/2.0.61(Unix)
Access-Control-Allow-Origin: http://liudan.handou.com
Access-Control-Allow-Credentials:true
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers:X-Custom-Header
Access-Control-Max-Age: 172800
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length:0
Keep-Alive:timeout=2,max=100
Connection:keep-alive
Content-Type:text/plain
复制代码

在响应头中除了Access-Control-Allow-Origin和Access-Control-Allow-Credentials外,又额外多出了3个返回值:

Access-Control-Allow-Methods:容许访问的方式

Access-Control-Allow-Headers:容许携带的请求头

Access-Control-Max-Age:本次许可的有效市场,单位:s,过时以前的ajax就无需再次进行预检了

若是浏览器获得了上述响应,就认定为能够跨域,后续就跟简单请求同样处理,如下为复杂请求的具体图示:

10.png

相关文章
相关标签/搜索