原文:https://www.cnblogs.com/javalinux/p/15871235.html
AJAX請求中出現(xiàn)OPTIONS請求
背景
有一個(gè)前后端分離的VUE項(xiàng)目來發(fā)送ajax
請求, 查看Nginx
日志或使用Chrome Dev Tools
查看請求發(fā)送情況時(shí), 會(huì)看到每次調(diào)后臺(tái)API的請求之前, 都會(huì)發(fā)送一個(gè)OPTIONS請求, 無論API要求請求的方法是GET或POST.
為什么會(huì)發(fā)送這個(gè)OPTIONS請求? 困擾了項(xiàng)目組的前端同學(xué)和后端同學(xué)很久元潘,今天正好聽他們說起這個(gè)問題畔乙,就研究了一下原理。
過程
首先, 在js代碼里是沒寫要發(fā)送OPTIONS請求的, 后臺(tái)API要的請求方法不是GET就是POST, 也只發(fā)送過這兩種類型的請求.
那么, 我就以為是前臺(tái)調(diào)用方式的問題, 于是去看了看代碼. 發(fā)現(xiàn)并沒有什么很特殊的地方翩概。然后直接看ajax.js
文件. 結(jié)果, 發(fā)現(xiàn)一切正常, 并沒有這種邏輯: 發(fā)送GET/POST請求之前, 先發(fā)送一個(gè)OPTIONS請求.
看來就不是前端同學(xué)寫法的問題啸澡,也不是ajax的問題袖订,仔細(xì)對比了一下之前的代碼,發(fā)現(xiàn)唯一的區(qū)別就是ajax中用到了自定義的header頭嗅虏。
解惑
眾所周知, ajax請求是由XMLHttpRequest
對象實(shí)現(xiàn)的(部分低版本ID瀏覽器不是), 而XMLHttpRequest
會(huì)遵守同源策略(same-origin policy). 也即腳本只能訪問相同協(xié)議/相同主機(jī)名/相同端口的資源, 如果要突破這個(gè)限制, 那就是所謂的跨域, 此時(shí)需要遵守CORS(Cross-Origin Resource Sharing)機(jī)制。
那么, 允許跨域, 不就是服務(wù)端(例如Nginx或者后端代碼)設(shè)置Access-Control-Allow-Origin: *
就可以了嗎?
普通的請求確實(shí)是這樣子的, 除此之外, 還一種叫請求叫Preflighted Request(帶預(yù)檢的跨域請求)
Preflighted Request在發(fā)送真正的請求前, 會(huì)先發(fā)送一個(gè)方法為OPTIONS的預(yù)請求(Preflighted Request), 用于試探服務(wù)端是否能接受真正的請求.
如果options獲得的回應(yīng)是拒絕性質(zhì)的上沐,比如404\403\500等http狀態(tài)皮服,就會(huì)停止post、get等請求的發(fā)出参咙。
那么, 什么情況下請求會(huì)變成Preflighted Request呢? 翻看了MDN的文檔發(fā)現(xiàn)如下:(文檔地址:https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#The_HTTP_request_headers)
- 請求方法不是GET/HEAD/POST
- POST請求的
Content-Type
并非application/x-www-form-urlencoded
,multipart/form-data
, 或text/plain
- 請求設(shè)置了自定義的
header
字段
舉個(gè)例子, 如果POST請求要傳輸?shù)臄?shù)據(jù)為 XML文檔, Content-Type
為application/xml
或text/xml
, 則發(fā)送這個(gè)請求前會(huì)發(fā)送一個(gè)預(yù)請求龄广,或者自定義的header字段也是一樣的道理。
有了上面的知識點(diǎn), 再去看項(xiàng)目中ajax調(diào)用
可以看出, 跨域請求中設(shè)置了自定義的header
字段, 所以該請求是preflighted request
, 則請求前一定會(huì)發(fā)送一個(gè)OPTIONS作為預(yù)請求.
所以說, 在項(xiàng)目中ajax對后臺(tái)API的調(diào)用, OPTIONS請求是沒辦法去掉的, 除非后臺(tái)接口不再需要在請求header
中設(shè)置openId
但是由于該項(xiàng)目中用戶信息是采用的JWT的方式蕴侧,所以只好作罷择同。
但是由于該項(xiàng)目在后臺(tái)中自定義了請求頻率限制的攔截器,例如限制同一個(gè)客戶端一秒內(nèi)對某一個(gè)接口只能訪問1次净宵。如果超過限制敲才,則第二次會(huì)返回狀態(tài)碼500,不予處理择葡。如果每次請求前都帶著一次OPTIONS請求紧武,則該攔截器無法正常實(shí)現(xiàn)功能,反正會(huì)導(dǎo)致大批接口調(diào)用失敗的情況敏储。
鑒于上述分析阻星,既然前端發(fā)起請求時(shí)OPTIONS請求沒有辦法去除,那么是否可以考慮從后臺(tái)攔截器進(jìn)行改造已添。
改造后的代碼如下:
如果攔截到的請求不是項(xiàng)目中常規(guī)的GET或者POST請求妥箕,則該攔截器直接放行。至此更舞,問題完美解決畦幢。希望可以幫到有類似問題的小伙伴~