CntChen / cntchen.github.io

CntChen Blog
https://github.com/CntChen/cntchen.github.io/issues
732 stars 64 forks source link

IE9 跨域请求兼容 #14

Open CntChen opened 6 years ago

CntChen commented 6 years ago

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

怀疑是 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...
  }
}

需要注意的是:

题外话: whatwg-fetch 一直采用 XMLHttpRequest 来做 polyfill, whatwg-fetch1.0+ 不支持 IE9, 并不是因为没有采用 XDomainRequest, 而是因为 [IE9 的状态码不符合 fetch 规范][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 掉.

解决办法是:

最终代码

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...
  }
}

结论

References

EOF

haigendong commented 6 years ago

老铁666

jYh1014 commented 5 years ago

不行啊,我这边还是报403

monkback commented 4 years ago

不行啊,我这边还是报403

我们也是,前端换成XDomainRequest支持跨域后,就报403了