爲何一個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

相關文章
相關標籤/搜索