本文收錄在www.devsai.com個人博客中
寫在前面
有沒有一看到講跨域資源共享的就不想再看的了拒垃,網(wǎng)上的跨域資源共享的博文祠墅,三天兩頭的就出一篇难审。
既然你已經(jīng)進(jìn)來看了沙绝,還請你稍稍忍耐下慢蜓,繼續(xù)往下看亚再,或許你會發(fā)現(xiàn)和之前看到的有不一樣的收獲。
其實(shí)晨抡,之前看過我寫的文章的同學(xué)可能知道氛悬,我寫過一篇關(guān)于《跨域及跨域資源共享》(沒有看過的同學(xué),可以從這進(jìn)去)耘柱。比較全面的介紹了跨域的多種解決方案如捅,以及說明了跨域資源共享.
你們會不會想:那既然已經(jīng)寫過了,為什么又寫一篇调煎? 是不是博主已經(jīng)沒啥東西可寫了镜遣。
別急,接下來士袄,讓我跟你們慢慢道來悲关。
你們所知道的
看過之前寫的《跨域及跨域資源共享》或看過多篇CORS文章的同學(xué)可以選擇性的跳過這一小段了。
就像你們看到過的相關(guān)的文章娄柳,講跨域資源共享寓辱,一般講其原理時,必定要講到跨域資源共享的請求有兩種(也有很多沒有講到):
簡單請求 (Simple Request)
預(yù)檢測請求 (Preflight Request)
然后就會進(jìn)一步的講到赤拒,什么時候發(fā)只發(fā)簡單請求秫筏,又什么時候會在發(fā)真實(shí)的請求前诱鞠,先發(fā)預(yù)檢測請求,普遍的都是這么說的(包括我之前寫的也是)
以下幾種情況時都滿足時是簡單請求
request header 是簡單的請求頭
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type
等等非自定義的請求頭
request method 是下面的請求類型
HEAD
GET
POST
Content-Type 只限三個值
application/x-www-form-urlencoded跳昼、multipart/form-data般甲、text/plain
如果不滿足以上條件的都會先發(fā)送預(yù)檢測請求,即為OPTIONS請求類型的請求
幾乎都是這么說的鹅颊,差別只是描述方式不同敷存,比例下面的別人寫的:
什么? 不信堪伍, 那你隨便搜索幾篇相關(guān)的文章看看锚烦。
那么,這有什么問題
先來做個例子吧帝雇。
假設(shè)要實(shí)現(xiàn)帶進(jìn)度條的上傳功能涮俄,接口不是同域上的,服務(wù)端已經(jīng)給配置了支持跨域資源共享的響應(yīng)頭尸闸,那我們直接用XmlHttpRequest就可以了
javaScript代碼大概如下:
varxhr =newXmlHttpRequest();xhr.onreadystatechange =function(){// do something}xhr.upload.onprogress =function(){// do something}xhr.open('POST','http://127.0.0.1/upload');varfd =newFormData();fd.append('file',file);xhr.send(fd);
然后上傳文件并查看下請求
What? 為什么會有兩個請求啊彻亲。 是不是它不滿足簡單請求的要求(已不記得簡單請求的同學(xué)往上再看看)
那么,我們來看看該真實(shí)請求的請求頭
簡單請求要求:
要求點(diǎn)實(shí)際內(nèi)容是否滿足
請求方式POST滿足
請求頭都是非自定義的請求頭滿足
Content-Typemultipart/form-data滿足
不是都滿足了嗎吮廉?那為什么苞尝,會有兩個請求,為什么在發(fā)送真實(shí)請求前還發(fā)了OPTIONS方式的請求宦芦。
為什么V嬷贰!调卑!整個人感覺都不好了B丈啊!恬涧!
變個魔術(shù)
把上面的javaScript代碼改動下:
varxhr =newXmlHttpRequest();xhr.onreadystatechange =function(){// do something}xhr.open('POST','http://127.0.0.1/upload');varfd =newFormData();fd.append('file',file);xhr.send(fd);
去掉了xhr.upload.onprogress注益,上傳后再來看下請求及請求頭:
只有一個請求,請求頭內(nèi)容還都一樣溯捆。 (這到底是怎么回事...有種再也不相信愛情的趕覺了A那场!现使!)
看到這里的同學(xué),有木有覺得博主在坑你們旷痕,放了兩張相同的請求頭截圖就想糊弄碳锈。
俗話說得好,不試不知道欺抗,一試嚇一跳售碳,要不,你們也親自試試,一試便知真假贸人。
再次雙手奉上demo(喜歡的順便點(diǎn)個贊哦~)间景。
分析問題
從上面的兩小段JS看出,只是去除了上傳的進(jìn)度信息事件艺智。也就是說加了進(jìn)度事件就多發(fā)了個預(yù)檢測請求倘要。
那么,還有沒有其他的事件了十拣?添加其他的事件會不會也會發(fā)送預(yù)檢測請求呢封拧?
事件有onerror,onloadstart等等。經(jīng)過博主的測試夭问,上述答案是肯定的泽西,添加其他事件后,確實(shí)也會發(fā)生預(yù)檢測請求缰趋。
追求真理的同學(xué)們捧杉,在博主的demo里改改試試吧。
現(xiàn)在已經(jīng)知道了問題的所在秘血,由上傳相關(guān)事件導(dǎo)致了跨域請求多發(fā)了預(yù)檢測請求味抖,說好的簡單請求(Simple Request)呢~
博主抱著對問題刨根問底的精神,再次查看cors相關(guān)文檔直撤,找到了如下的內(nèi)容:
If the following conditions are true, follow the simple cross-origin request algorithm:
The request method is a simple method and the force preflight flag is unset.
Each of the author request headers is a simple header or author request headers is empty.
通過這段非竿,我們知道,原來除了我們所知道的簡單請求的幾大特征外谋竖,還提到了force preflight flag红柱,這是什么鬼?
難道是因為設(shè)置了它蓖乘? 那么什么時候設(shè)置了force preflight flag?
上面我們知道了因為上傳事件導(dǎo)致了發(fā)送預(yù)檢測請求锤悄,會不會是上傳監(jiān)聽事件的時候給設(shè)置了force preflight flag,
然后在XmlHttpRequest level 2中的找到了相關(guān)的內(nèi)容嘉抒,以證實(shí)我的猜測是正確的零聚。
有下面幾段內(nèi)容:
force preflight flag
The upload events flag.
從這段可以知道,force preflight flag與upload events flag是對應(yīng)的,看到這里就知道了些侍,只要upload events flag被設(shè)置true
那么就等于force preflight flag被設(shè)置了true隶症,這時,不管請求的類型的是不是simple method岗宣,也不管請求頭是不是simple header蚂会,都會先發(fā)送預(yù)檢測請求。
接下來耗式,我們再來看看upload events flag會在什么情況下被設(shè)置呢胁住?
If the asynchronous flag is true and one or more event listeners are registered on the XMLHttpRequestUpload object set the upload events flag to true. Otherwise, set the upload events flag to false.
原來趁猴,當(dāng)asynchronous flag為true并且XMLHttpRequestUpload(即示例中的xhr.upload)的一個或多個事件被監(jiān)聽的時候,upload listener flag就會被設(shè)置了彪见。
這也正如之前測試的儡司,當(dāng)加了xhr.upload.onprogress后,出現(xiàn)了預(yù)檢測請求余指。
到這里總算水落石出了捕犬。
這里還需要說明的一點(diǎn)是,the asynchronous flag就是xhr.open()的第三個參數(shù)浪规,當(dāng)未設(shè)置第三參數(shù)時或听,默認(rèn)為異步,也就是the asynchronous flag為true
如果第三個參數(shù)設(shè)置為false笋婿,那么即使有上傳的監(jiān)聽事件也不會發(fā)送預(yù)檢測請求(Preflight Reuqest)
總結(jié)
以后還會不會理直氣壯的在別人面前說誉裆,只要是滿足幾大條件(是非自定義的請求頭,是GETorPOSTorHEAD,或Content-Type是那三種值的)就是簡單請求 缸濒,就不會發(fā)生預(yù)檢測請求足丢。
通過本文可知,并非滿足這幾大條件就一定是簡單請求的庇配,
應(yīng)該要加個前置條件斩跌,是否是在上傳請求中跨域,是否是異步的捞慌,是否監(jiān)聽了上傳事件耀鸦。
看到這,可能你想說啸澡,寫這么多有啥用袖订,對實(shí)際開發(fā)有幫助嗎?或許沒什么實(shí)際的幫助吧嗅虏,又或許你也不會碰到吧洛姑。
但,最起碼當(dāng)你碰到的時候皮服,你看到了兩個請求楞艾,再看了下代碼,你已經(jīng)心里就有數(shù)了龄广,知道這是怎么一回事了硫眯。
一直認(rèn)為,做技術(shù)的對碰到的問題要知其然择同,更要知其所以然舟铜。