查看原文
其他

IE9 跨域请求兼容

前端大全 2019-11-13

(点击上方公众号,可快速关注)


作者:CntChen

github.com/CntChen/cntchen.github.io/issues/14


IE9 跨域请求兼容


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: 拒绝访问:


  • IE9

  • IE11 开 IE9 调试模式

怀疑是 fetch 的兼容问题, 查看一下版本:


$npm list whatwg-fetch    

project

└── whatwg-fetch@2.0.3


查看了一下whatwg-fetch 兼容性: 只支持到 IE10. 然后看到 whatwg-fetchv0.11 可以兼容 IE9, 那就降级一下吧:


$ 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 实现异步请求, 代码:


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 mehtod

  • XDomainRequest 不支持带 cookie

  • XDomainRequest 不能设置 responseType, 通信双方需要约定数据格式

  • XDomainRequest 的响应没有 response status code


题外话: whatwg-fetch 一直采用 XMLHttpRequest 来做 polyfill, whatwg-fetch1.0+ 不支持 IE9, 并不是因为没有采用 XDomainRequest, 而是因为 IE9 的状态码不符合 fetch 规范, 而 polyfill 的目标是 polyfill 规范, 而不是做兼容.


问题: 请求异常终止和挂起


写好了代码, 在 IE9 中, 网络请求非常诡异, 经常不行: 请求只持续了不到 1ms, 并且接收数据为 0B, 没有状态码; 但是在少数时候是可以成功请求并获取数据的.


  • IE9

  • IE11 开 E9 调试模式

此时 IE11 的 IE9 调试模式是可以的, 看来模拟器还是模拟不到位.

查了好久, 终于看到一篇文章: Internet Explorer Aborting AJAX Requests : FIXED


IE timing out the request even though data is being transmitted.


主要的原因大概是 IE9 会将一个正在传输的请求 timeout 掉.


解决办法是:


  • 添加 onprogress 事件回调, 告知 IE9 这个请求是活动中的, 不要 timeout 掉.

  • 将请求的发送放到主线程之外, 保证 XDomainRequest 已经完全初始化好.


最终代码


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

    https://blogs.msdn.microsoft.com/ieinternals/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds/


  • Internet Explorer Aborting AJAX Requests : FIXED

    https://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/



觉得本文对你有帮助?请分享给更多人

关注「前端大全」,提升前端技能

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存