Переопределить / перехватить ответ XMLHttpRequest во всех браузерах - PullRequest
0 голосов
/ 15 мая 2018

Чего я хочу достичь?

Я хочу перехватить XMLHttpRequest и изменить ответ для некоторых конкретных запросов. (Например, расшифровать контент и назначить его обратному ответу)

Что я уже сделал?

Ниже код перехватывает запрос и модифицирует ответ. Работает во всех браузерах (Chrome, Firefox, Opera, Edge), кроме IE 11.

  const dummySend = XMLHttpRequest.prototype.send;
  XMLHttpRequest.prototype.send = function () {
    const _onreadystatechange = this.onreadystatechange;

    this.onreadystatechange = function () {
      if (this.readyState === 4) {
        if (this.status === 200 || this.status === 1223) {
          // as response is read-only and configurable, make it writable
          Object.defineProperty(this, 'response', {writable: true});
          this.response = modifyResponse(this.response);
        }
      }
      if (_onreadystatechange) {
        _onreadystatechange.apply(this, arguments);
      }
    }
    dummySend.apply(__self, arguments);
  }

В чем проблема?

Все это не работает только в IE 11. Выдается сообщение об ошибке «Ошибка типа: в строгом режиме не допускается присвоение свойству только для чтения».

Может кто-нибудь, пожалуйста, помогите мне с этим?

1 Ответ

0 голосов
/ 17 мая 2018

Я мог бы сделать это другим способом, который заключается в том, чтобы сделать фиктивный объект XMLHttpRequest доступным для исходного запросчика, а затем самостоятельно обработать фактический запрос XMLHttpRequest. Пожалуйста, прочтите код для большей ясности.

  let oldXMLHttpRequest = window.XMLHttpRequest;

  // define constructor for XMLHttpRequest proxy object
  window.XMLHttpRequest = function() {
    let _originalXhr = new oldXMLHttpRequest();
    let _dummyXhr = this;

    function decryptResponse(actualResponse) {
      return base64Decrypted = decrypt(response, secret);
    }

    _dummyXhr.response = null;

    // expose dummy open
    _dummyXhr.open = function () {
      const _arguments = [].slice.call(arguments);

      // do any url modifications here before request open

      _dummyXhr._url = _arguments[1];
      return _originalXhr.open.apply(_originalXhr, _arguments);
    };

    // expose dummy send
    _dummyXhr.send = function () {
      let _onreadystatechange = _dummyXhr.onreadystatechange;

      _originalXhr.onreadystatechange = function() {
        if (this.readyState === 4 && (this.status === 200 || this.status === 1223)) {
          _dummyXhr.response = decryptResponse(this.response);
        }
        // call callback that was assigned on our object
        if (_onreadystatechange) {
          _onreadystatechange.apply(_dummyXhr, arguments);
        }
      }

      _originalXhr.send.apply(_originalXhr, arguments);
    };

    // iterate all properties in _originalXhr to proxy them according to their type
    // For functions, we call _originalXhr and return the result
    // For non-functions, we make getters/setters
    // If the property already exists on _dummyXhr, then don't proxy it
    for (let prop in _originalXhr) {
      // skip properties we already have - this will skip both the above defined properties
      // that we don't want to proxy and skip properties on the prototype belonging to Object
      if (!(prop in _dummyXhr)) {
        // create closure to capture value of prop
        (function(prop) {
          if (typeof _originalXhr[prop] === "function") {
            // define our own property that calls the same method on the _originalXhr
            Object.defineProperty(_dummyXhr, prop, {
              value: function() {return _originalXhr[prop].apply(_originalXhr, arguments);}
            });
          } else {
            // define our own property that just gets or sets the same prop on the _originalXhr
            Object.defineProperty(_dummyXhr, prop, {
              get: function() {return _originalXhr[prop];},
              set: function(val) {_originalXhr[prop] = val;}
            });
          }
        })(prop);
      }
    }
...