明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

淺談對(duì)于預(yù)檢請(qǐng)求

[摘要]背景不知道大家有沒(méi)有發(fā)現(xiàn),有時(shí)候我們?cè)谡{(diào)用后臺(tái)接口的時(shí)候,會(huì)請(qǐng)求兩次,如下圖的其實(shí)第一次發(fā)送的就是preflight request(預(yù)檢請(qǐng)求),那么這篇文章將講一下,為什么要發(fā)預(yù)檢請(qǐng)求,什么時(shí)候會(huì)發(fā)預(yù)檢請(qǐng)求,預(yù)檢請(qǐng)求都做了什么一. 為什么要發(fā)預(yù)檢請(qǐng)求我們都知道瀏覽器的同源策略,就是出于安全考慮,...


image.png

背景

不知道大家有沒(méi)有發(fā)現(xiàn),有時(shí)候我們?cè)谡{(diào)用后臺(tái)接口的時(shí)候,會(huì)請(qǐng)求兩次,如下圖的

圖一.png

其實(shí)第一次發(fā)送的就是preflight request(預(yù)檢請(qǐng)求),那么這篇文章將講一下,為什么要發(fā)預(yù)檢請(qǐng)求,什么時(shí)候會(huì)發(fā)預(yù)檢請(qǐng)求,預(yù)檢請(qǐng)求都做了什么

一. 為什么要發(fā)預(yù)檢請(qǐng)求

我們都知道瀏覽器的同源策略,就是出于安全考慮,瀏覽器會(huì)限制從腳本發(fā)起的跨域HTTP請(qǐng)求,像XMLHttpRequest和Fetch都遵循同源策略。
瀏覽器限制跨域請(qǐng)求一般有兩種方式:
1. 瀏覽器限制發(fā)起跨域請(qǐng)求
2. 跨域請(qǐng)求可以正常發(fā)起,但是返回的結(jié)果被瀏覽器攔截了

一般瀏覽器都是第二種方式限制跨域請(qǐng)求,那就是說(shuō)請(qǐng)求已到達(dá)服務(wù)器,并有可能對(duì)數(shù)據(jù)庫(kù)里的數(shù)據(jù)進(jìn)行了操作,但是返回的結(jié)果被瀏覽器攔截了,那么我們就獲取不到返回結(jié)果,這是一次失敗的請(qǐng)求,但是可能對(duì)數(shù)據(jù)庫(kù)里的數(shù)據(jù)產(chǎn)生了影響。

為了防止這種情況的發(fā)生,規(guī)范要求,對(duì)這種可能對(duì)服務(wù)器數(shù)據(jù)產(chǎn)生副作用的HTTP請(qǐng)求方法,瀏覽器必須先使用OPTIONS方法發(fā)起一個(gè)預(yù)檢請(qǐng)求,從而獲知服務(wù)器是否允許該跨域請(qǐng)求:如果允許,就發(fā)送帶數(shù)據(jù)的真實(shí)請(qǐng)求;如果不允許,則阻止發(fā)送帶數(shù)據(jù)的真實(shí)請(qǐng)求。

二. 什么時(shí)候發(fā)預(yù)檢請(qǐng)求

HTTP請(qǐng)求包括: 簡(jiǎn)單請(qǐng)求 和 需預(yù)檢的請(qǐng)求

1. 簡(jiǎn)單請(qǐng)求

簡(jiǎn)單請(qǐng)求不會(huì)觸發(fā)CORS預(yù)檢請(qǐng)求,“簡(jiǎn)屬于
單請(qǐng)求”術(shù)語(yǔ)并不屬于Fetch(其中定義了CORS)規(guī)范。
若滿足所有下述條件,則該請(qǐng)求可視為“簡(jiǎn)單請(qǐng)求”:
- 使用下列方法之一:
- GET
- HEAD
- POST
- Content-Type: (僅當(dāng)POST方法的Content-Type值等于下列之一才算做簡(jiǎn)單需求)
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded

注意: WebKit Nightly 和 Safari Technology Preview 為Accept
, Accept-Language
, 和 Content-Language
首部字段的值添加了額外的限制。如果這些首部字段的值是“非標(biāo)準(zhǔn)”的,WebKit/Safari 就不會(huì)將這些請(qǐng)求視為“簡(jiǎn)單請(qǐng)求”。WebKit/Safari 并沒(méi)有在文檔中列出哪些值是“非標(biāo)準(zhǔn)”的,不過(guò)我們可以在這里找到相關(guān)討論:Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language, Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS, and Switch to a blacklist model for restricted Accept headers in simple CORS requests。其它瀏覽器并不支持這些額外的限制,因?yàn)樗鼈儾粚儆谝?guī)范的一部分。

2.需預(yù)檢的請(qǐng)求

“需預(yù)檢的請(qǐng)求”要求必須首先使用OPTIONS方法發(fā)起一個(gè)預(yù)檢請(qǐng)求到服務(wù)區(qū),以獲知服務(wù)器是否允許該實(shí)際請(qǐng)求。“預(yù)檢請(qǐng)求”的使用,可以避免跨域請(qǐng)求對(duì)服務(wù)器的用戶數(shù)據(jù)產(chǎn)生未預(yù)期的影響。

當(dāng)請(qǐng)求滿足下述任一條件時(shí),即應(yīng)首先發(fā)送預(yù)檢請(qǐng)求:
- 使用了下面任一 HTTP 方法:
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
- 人為設(shè)置了對(duì) CORS 安全的首部字段集合之外的其他首部字段。該集合為:
- Accept
- Accept-Language
- Content-Language
- Content-Type
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- Content-Type的值不屬于下列之一:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain

如下是一個(gè)需要執(zhí)行預(yù)檢請(qǐng)求的HTTP請(qǐng)求:

var invocation = new XMLHttpRequest();
var url = ' 
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';function callOtherDomain(){
  if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PRODUCT', 'H5');
      invocation.setRequestHeader('Content-Type', 'application/xml');
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }
}
......

上面的代碼使用POST請(qǐng)求發(fā)送一個(gè)XML文檔,該請(qǐng)求包含了一個(gè)自定義的首部字段(X-PRODUCT:H5)。另外,該請(qǐng)求的Content-Typeapplication/xml。因此,該請(qǐng)求需要首先發(fā)起“預(yù)檢請(qǐng)求”。
image.png

1. OPTIONS /resources/post-here/ 
2. HTTP/1.13. Host: bar.other4. User-Agent: Mozilla/5.0 (Macintosh; U; 5.Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre6. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.87. Accept-Language: en-us,en;q=0.58. Accept-Encoding: gzip,deflate9. Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.710. Connection: keep-alive11. Origin: http://foo.example12. Access-Control-Request-Method: POST13. Access-Control-Request-Headers: X-PINGOTHER, Content-Type14. HTTP/1.1 200 OK15. Date: Mon, 01 Dec 2008 01:15:39 GMT16. Server: Apache/2.0.61 (Unix)17. Access-Control-Allow-Origin: http://foo.example18. Access-Control-Allow-Methods: POST, GET, OPTIONS19. Access-Control-Allow-Headers: X-PINGOTHER, Content-Type20. Access-Control-Max-Age: 8640021. Vary: Accept-Encoding, Origin22. Content-Encoding: gzip23. Content-Length: 024. Keep-Alive: timeout=2, max=10025. Connection: Keep-Alive26. Content-Type: text/plain

從上面的報(bào)文中可以看到,第1~12行發(fā)送了一個(gè)使用OPTIONS方法的預(yù)檢請(qǐng)求。 OPTIONS是HTTP/1.1協(xié)議中定義的方法,用以從服務(wù)器獲取更多信息。該方法不會(huì)對(duì)服務(wù)器資源產(chǎn)生影響。遇見(jiàn)請(qǐng)求中同時(shí)攜帶了下面兩個(gè)首部字段:

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PRODUCT

首部字段 Access-Control-Request-Method 告知服務(wù)器,實(shí)際請(qǐng)求將使用 POST 方法。首部字段 Access-Control-Request-Headers 告知服務(wù)器,實(shí)際請(qǐng)求將攜帶兩個(gè)自定義請(qǐng)求首部字段:X-PINGOTHER 與 Content-Type。服務(wù)器據(jù)此決定,該實(shí)際請(qǐng)求是否被允許。

第14~26行 為預(yù)檢請(qǐng)求的響應(yīng),表明服務(wù)器將堅(jiān)守后續(xù)的實(shí)際請(qǐng)求。重點(diǎn)看第17~20行:

Access-Control-Allow-Origin: http://foo.exampleAccess-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-TypeAccess-Control-Max-Age: 86400

首部字段 Access-Control-Allow-Methods 表明服務(wù)器允許客戶端使用 POST,GET 和 OPTIONS 方法發(fā)起請(qǐng)求。

首部字段Access-Control-Allow-Headers 表明服務(wù)器允許請(qǐng)求中攜帶字段X-PINGOTHERContent-Type。與 Access-Control-Allow-Methods一樣,Access-Control-Allow-Headers的值為逗號(hào)分割的列表。

最后,首部字段

Access-Control-Max-Age 表明該響應(yīng)的有效時(shí)間為 86400 秒,也就是 24 小時(shí)。在有效時(shí)間內(nèi),瀏覽器無(wú)須為同一請(qǐng)求再次發(fā)起預(yù)檢請(qǐng)求。請(qǐng)注意,瀏覽器自身維護(hù)了一個(gè)最大有效時(shí)間,如果該首部字段的值超過(guò)了最大有效時(shí)間,將不會(huì)生效。

預(yù)檢請(qǐng)求完成之后,發(fā)送實(shí)際請(qǐng)求:

POST /resources/post-here/ HTTP/1.1Host: bar.otherUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3preAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-us,en;q=0.5Accept-Encoding: gzip,deflateAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7Connection: keep-aliveX-PINGOTHER: pingpongContent-Type: text/xml; charset=UTF-8Referer: http://foo.example/examples/preflightInvocation.htmlContent-Length: 55Origin: http://foo.examplePragma: no-cacheCache-Control: no-cache<?xml version="1.0"?><person><name>Arun</name></person>HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
[Some GZIP'd payload]

以上就是淺談關(guān)于預(yù)檢請(qǐng)求的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!


網(wǎng)站建設(shè)是一個(gè)廣義的術(shù)語(yǔ),涵蓋了許多不同的技能和學(xué)科中所使用的生產(chǎn)和維護(hù)的網(wǎng)站。