拦截XMLHttpRequest并修改responseText

问题描述 投票:0回答:5

我正在尝试构建一个脚本,该脚本将充当本机

XMLHttpRequest
对象的代理/包装器,使我能够拦截它,修改responseText并返回到原始的onreadystatechange事件。

上下文是,如果应用程序尝试接收的数据已在本地存储中可用,则中止

XMLHttpRequest
并将本地存储的数据传递回应用程序的成功/失败回调方法。假设我无法控制应用程序现有的 AJAX 回调方法。

我最初尝试过以下想法..

var send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(data){
   //Do some stuff in here to modify the responseText
   send.call(this, data);
};

但正如我现在所确定的,responseText 是只读的。

然后我尝试后退一步,编写我自己的完整本机代理到

XMLHttpRequest
,最终编写我自己版本的本机方法。与这里讨论的类似...

http://www.ilinsky.com/articles/XMLHttpRequest/#implementation-wrapping

但是它很快就变得混乱了,并且仍然很难将修改后的数据返回到原始的

onReadyStateChange
方法中。

有什么建议吗?这可能吗?

javascript ajax xmlhttprequest
5个回答
14
投票

//
// firefox, ie8+ 
//
var accessor = Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, 'responseText');

Object.defineProperty(XMLHttpRequest.prototype, 'responseText', {
	get: function() {
		console.log('get responseText');
		return accessor.get.call(this);
	},
	set: function(str) {
		console.log('set responseText: %s', str);
		//return accessor.set.call(this, str);
	},
	configurable: true
});


//
// chrome, safari (accessor == null)
//
var rawOpen = XMLHttpRequest.prototype.open;

XMLHttpRequest.prototype.open = function() {
	if (!this._hooked) {
		this._hooked = true;
		setupHook(this);
	}
	rawOpen.apply(this, arguments);
}

function setupHook(xhr) {
	function getter() {
		console.log('get responseText');

		delete xhr.responseText;
		var ret = xhr.responseText;
		setup();
		return ret;
	}

	function setter(str) {
		console.log('set responseText: %s', str);
	}

	function setup() {
		Object.defineProperty(xhr, 'responseText', {
			get: getter,
			set: setter,
			configurable: true
		});
	}
	setup();
}


2
投票

以下脚本在通过 XMLHttpRequest.prototype.send 发送之前完美拦截数据

<script>
(function(send) { 

        XMLHttpRequest.prototype.send = function(data) { 

            this.addEventListener('readystatechange', function() { 

            }, false); 

            console.log(data); 
            alert(data); 

        }; 

})(XMLHttpRequest.prototype.send);
</script>

2
投票

在我看来,要拦截

XMLHttpRequest
的响应,更现代的解决方案将扩展原始 XMLHttpRequest 并将其覆盖在
window
对象中:

const { interceptXhrResponse } = (function () {
  let interceptionRules = [];

  /**
   * Function to intercept responses for given URL patterns
   * @param {RegExp} urlPattern - Regular expression to match the (canonicalized) URL
   * @param {Function} responseHandler - Function to handle the intercepted response
   */
  function interceptXhrResponse(urlPattern, responseHandler) {
    interceptionRules.push({ urlPattern, responseHandler });
  }

  // Function to find specific handler for the URL and return modified response
  function handleInterceptedResponse(response, url) {
    const interceptionRule = interceptionRules.find(({ urlPattern }) =>
      urlPattern.test(url)
    );

    if (interceptionRule) {
      const { responseHandler } = interceptionRule;
      return responseHandler(response, url);
    }

    return response;
  }

  const OriginalXMLHttpRequest = window.XMLHttpRequest;

  class XMLHttpRequest extends OriginalXMLHttpRequest {
    get responseText() {
      // If the request is not done, return the original responseText
      if (this.readyState !== 4) {
        return super.responseText;
      }

      return handleInterceptedResponse(super.responseText, this.responseURL);
    }

    get response() {
      // If the request is not done, return the original response
      if (this.readyState !== 4) {
        return super.response;
      }

      return handleInterceptedResponse(super.response, this.responseURL);
    }
  }

  window.XMLHttpRequest = XMLHttpRequest;

  return { interceptXhrResponse };
})();

上面的代码公开了

interceptXhrResponse
函数,允许您使用正则表达式和相应的响应处理程序指定 URL 模式。您可以在处理程序中返回您想要的所有内容来更改响应。

例如:

interceptXhrResponse(/.+/, (response, url) => {
  return `Response of ${url}: Intercepted. Original response length: ${String(response).length}`
})

然后我们可以尝试发起一个

XMLHttpRequest
:

const xhr = new XMLHttpRequest()
xhr.open('GET', 'https://stackoverflow.com/404')
xhr.send()
xhr.onloadend = () => {
  console.log(xhr.responseText)
}

输出:

Response of https://stackoverflow.com/404: Intercepted. Original response length: 63486

0
投票

您的后退是一种矫枉过正:您可以在 XMLHttpRequest 上添加您自己的 getter:(有关属性的更多信息

Object.defineProperty(XMLHttpRequest.prototype,"myResponse",{
  get: function() {
    return this.responseText+"my update"; // anything you want
  }
});

用途:

var xhr = new XMLHttpRequest();
...
console.log(xhr.myResponse); // xhr.responseText+"my update"

注意在现代浏览器上您可以运行

xhr.onload
(请参阅XMLHttpRequest2提示


0
投票

由于某种原因,直到最近,被接受的答案一直在 Twitter 上对我有用。所以这是另一种有效的方法:

        var open_prototype = XMLHttpRequest.prototype.open
        unsafeWindow.XMLHttpRequest.prototype.open = function() {
            this.addEventListener('readystatechange', function(event) {
                if ( this.readyState === 4 ) {
                    var response = event.target.responseText.replaceAll("a", "b");
                    Object.defineProperty(this, 'response', {writable: true});
                    Object.defineProperty(this, 'responseText', {writable: true});
                    this.response = this.responseText = response;
                }
            });
            return open_prototype.apply(this, arguments);
        };
© www.soinside.com 2019 - 2024. All rights reserved.