IE9 跨域請求相容
1 2 |
Chrome: You will die! IE9: Not today! |
背景
搭建公司官網的框架時採用了 vuejs, 使用 history router mode 來做 SEO 優化, 使用 fetch 做網路請求, fetch 用 whatwg-fetch 做 polyfill. 根據百度瀏覽器市場份額統計, 2017年全年 IE9 的佔有率達到 9.50%, 並且 vue 框架也是相容到 IE9, 所以專案要求相容到 IE9.
但是 fetch polyfill 並不相容 IE9, 這篇文章追溯問題原因並提出解決方法.
問題: 訪問拒絕
在 IE9 下開啟頁面, 發現 fetch 請求報了Unhandled promise rejectionError: 拒絕訪問
:
懷疑是 fetch 的相容問題, 檢視一下版本:
1 2 3 |
$npm list whatwg-fetch project └── whatwg-fetch@2.0.3 |
檢視了一下whatwg-fetch 相容性: 只支援到 IE10. 然後看到 whatwg-fetchv0.11 可以相容 IE9, 那就降級一下吧:
1 2 3 4 5 6 |
$ npm uninstall whatwg-fetch removed 1 package in 4.851s $ npm install whatwg-fetch@0.11 + whatwg-fetch@0.11.1 added 1 package in 5.96s |
再試一下, 發現還是一樣的問題.
問題原因: IE9 XMLHttpRequest
不支援 CORS
fetch 的 polyfill 採用了 XMLHttpRequest 實現, 但是在 IE9 下面, XMLHttpRequest 是不支援跨域請求的. IE10 的 XMLHttpRequest
支援跨域, 而 IE8, IE9 需要使用 XDomainRequest 來實現跨域.
那就用 XDomainRequest
實現非同步請求, 程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
function fetchIe9(url, options = {}) => { if (window.XDomainRequest) { // https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest // only support GET and POST method // request and response content type should be JSON // without response status code return new Promise((resolve, reject) => { const method = options.method || 'GET'; const timeout = options.timeout || 30000; let data = options.body || options.params || {}; if (data instanceof Object) { data = JSON.stringify(data); } const XDR = new XDomainRequest(); XDR.open(method, url); XDR.timeout = timeout; XDR.onload = () => { try { const json = JSON.parse(XDR.responseText); return resolve(json.data); } catch (e) { reject(e); } return reject({}); }; XDR.ontimeout = () => reject('XDomainRequest timeout'); XDR.onerror = () => reject('XDomainRequest error'); XDR.send(data); }); } else { // native fetch or polyfill fetch(XMLHttpRequest) // fetch... } } |
需要注意的是:
XDomainRequest
只支援 GET 和 POST mehtodXDomainRequest
不支援帶 cookieXDomainRequest
不能設定responseType
, 通訊雙方需要約定資料格式XDomainRequest
的響應沒有 response status code
題外話: whatwg-fetch
一直採用 XMLHttpRequest
來做 polyfill, whatwg-fetch1.0+
不支援 IE9, 並不是因為沒有采用 XDomainRequest
, 而是因為 IE9 的狀態碼不符合 fetch 規範, 而 polyfill 的目標是 polyfill 規範, 而不是做相容.
問題: 請求異常終止和掛起
寫好了程式碼, 在 IE9 中, 網路請求非常詭異, 經常不行: 請求只持續了不到 1ms, 並且接收資料為 0B, 沒有狀態碼; 但是在少數時候是可以成功請求並獲取資料的.
查了好久, 終於看到一篇文章: Internet Explorer Aborting AJAX Requests : FIXED
IE timing out the request even though data is being transmitted.
主要的原因大概是 IE9 會將一個正在傳輸的請求 timeout 掉.
解決辦法是:
- 新增
onprogress
事件回撥, 告知 IE9 這個請求是活動中的, 不要 timeout 掉. - 將請求的傳送放到主執行緒之外, 保證 XDomainRequest 已經完全初始化好.
最終程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
function fetchIe9(url, options = {}) => { if (window.XDomainRequest) { // https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest // only support GET and POST method // request and response content type should be JSON // without response status code return new Promise((resolve, reject) => { const method = options.method || 'GET'; const timeout = options.timeout || 30000; let data = options.body || options.params || {}; if (data instanceof Object) { data = JSON.stringify(data); } const XDR = new XDomainRequest(); XDR.open(method, url); XDR.timeout = timeout; XDR.onload = () => { try { const json = JSON.parse(XDR.responseText); return resolve(json.data); } catch (e) { reject(e); } return reject({}); }; // fix random aborting: https://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/ XDR.onprogress = () => {}; XDR.ontimeout = () => reject('XDomainRequest timeout'); XDR.onerror = () => reject('XDomainRequest error'); setTimeout(() => { XDR.send(data); }, 0); }); } else { // native fetch or polyfill fetch(XMLHttpRequest) // fetch... } } |
結論
- IE9 發起跨域請求要使用
XDomainRequest
, 因為 IE9 下的XMLHttpRequest
不支援跨域呼叫. XDomainRequest
只支援 GET 和 POST method, 並且沒有 response status code, 可以說是不完善的 HTTP 非同步請求物件.- XDomainRequest 不支援指定 responseType, 使用時建議請求和返回資料格式約定為 JSON.
whatwg-fetch1.0+
不支援 IE9, 是因為 IE9 的狀態碼不符合 fetch 規範, 而 polyfill 的目標是 polyfill 規範, 而不是做相容.
References
- XDomainRequest
https://developer.mozilla.org/en-US/docs/Web/API/XDomainRequest
- XMLHttpRequest
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
- XDomainRequest – Restrictions, Limitations and Workarounds
- Internet Explorer Aborting AJAX Requests : FIXED
https://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/