背景
有一個前后端分離的VUE項目來發送ajax
請求, 查看Nginx
日志或使用Chrome Dev Tools
查看請求發送情況時, 會看到每次調后台API的請求之前, 都會發送一個OPTIONS請求, 無論API要求請求的方法是GET或POST.
為什么會發送這個OPTIONS請求? 困擾了項目組的前端同學和后端同學很久,今天正好聽他們說起這個問題,就研究了一下原理。
過程
首先, 在js代碼里是沒寫要發送OPTIONS請求的, 后台API要的請求方法不是GET就是POST, 也只發送過這兩種類型的請求.
那么, 我就以為是前台調用方式的問題, 於是去看了看代碼. 發現並沒有什么很特殊的地方。然后直接看ajax.js
文件. 結果, 發現一切正常, 並沒有這種邏輯: 發送GET/POST請求之前, 先發送一個OPTIONS請求.
看來就不是前端同學寫法的問題,也不是ajax的問題,仔細對比了一下之前的代碼,發現唯一的區別就是ajax中用到了自定義的header頭。
解惑
眾所周知, ajax請求是由XMLHttpRequest
對象實現的(部分低版本ID瀏覽器不是), 而XMLHttpRequest
會遵守同源策略(same-origin policy). 也即腳本只能訪問相同協議/相同主機名/相同端口的資源, 如果要突破這個限制, 那就是所謂的跨域, 此時需要遵守CORS(Cross-Origin Resource Sharing)機制。
那么, 允許跨域, 不就是服務端(例如Nginx或者后端代碼)設置Access-Control-Allow-Origin: *
就可以了嗎?
普通的請求確實是這樣子的, 除此之外, 還一種叫請求叫Preflighted Request(帶預檢的跨域請求)
Preflighted Request在發送真正的請求前, 會先發送一個方法為OPTIONS的預請求(Preflighted Request), 用於試探服務端是否能接受真正的請求.
如果options獲得的回應是拒絕性質的,比如404\403\500等http狀態,就會停止post、get等請求的發出。
那么, 什么情況下請求會變成Preflighted Request呢? 翻看了MDN的文檔發現如下:(文檔地址: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
- 請求設置了自定義的
header
字段
舉個例子, 如果POST請求要傳輸的數據為 XML文檔, Content-Type
為application/xml
或text/xml
, 則發送這個請求前會發送一個預請求,或者自定義的header字段也是一樣的道理。
有了上面的知識點, 再去看項目中ajax調用
可以看出, 跨域請求中設置了自定義的header
字段, 所以該請求是preflighted request
, 則請求前一定會發送一個OPTIONS作為預請求.
所以說, 在項目中ajax對后台API的調用, OPTIONS請求是沒辦法去掉的, 除非后台接口不再需要在請求header
中設置openId
但是由於該項目中用戶信息是采用的JWT的方式,所以只好作罷。
但是由於該項目在后台中自定義了請求頻率限制的攔截器,例如限制同一個客戶端一秒內對某一個接口只能訪問1次。如果超過限制,則第二次會返回狀態碼500,不予處理。如果每次請求前都帶着一次OPTIONS請求,則該攔截器無法正常實現功能,反正會導致大批接口調用失敗的情況。
鑒於上述分析,既然前端發起請求時OPTIONS請求沒有辦法去除,那么是否可以考慮從后台攔截器進行改造。
改造后的代碼如下:
如果攔截到的請求不是項目中常規的GET或者POST請求,則該攔截器直接放行。至此,問題完美解決。希望可以幫到有類似問題的小伙伴~