为页面上的所有AJAX请求添加"钩子"

Yul*_*liy 97 javascript ajax xmlhttprequest

我想知道是否可以"挂钩"每个AJAX请求(无论是要发送还是事件)并执行操作.此时我假设页面上还有其他第三方脚本.其中一些可能使用jQuery,而另一些则不然.这可能吗?

Joh*_*ner 110

如果您不必支持<= IE8,那么您可以执行此操作,这将一般性地拦截任何 AJAX全局,而不是搞砸任何可能由任何第三方AJAX库分配的回调等.接受的答案不会产生实际的响应,因为它过早被调用.

(function() {
    var origOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
        console.log('request started!');
        this.addEventListener('load', function() {
            console.log('request completed!');
            console.log(this.readyState); //will always be 4 (ajax is completed successfully)
            console.log(this.responseText); //whatever the response was
        });
        origOpen.apply(this, arguments);
    };
})();
Run Code Online (Sandbox Code Playgroud)

在这里使用addEventListener API可以执行的更多文档:

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress

  • 只是因为我对此进行了一些搜索。只有成功时才会调用“load”事件。如果您不关心结果(只是查询确实结束),您可以使用“loadend”事件 (2认同)

meo*_*ouw 107

受到aviv的回答的启发,我做了一些调查,这就是我想出来的.根据脚本中的注释,
我不确定它是否有用,当然只适用于使用本机XMLHttpRequest对象的浏览器.
我认为如果javascript库正在使用它将会起作用,因为如果可能的话它们将使用本机对象.

function addXMLRequestCallback(callback){
    var oldSend, i;
    if( XMLHttpRequest.callbacks ) {
        // we've already overridden send() so just add the callback
        XMLHttpRequest.callbacks.push( callback );
    } else {
        // create a callback queue
        XMLHttpRequest.callbacks = [callback];
        // store the native send()
        oldSend = XMLHttpRequest.prototype.send;
        // override the native send()
        XMLHttpRequest.prototype.send = function(){
            // process the callback queue
            // the xhr instance is passed into each callback but seems pretty useless
            // you can't tell what its destination is or call abort() without an error
            // so only really good for logging that a request has happened
            // I could be wrong, I hope so...
            // EDIT: I suppose you could override the onreadystatechange handler though
            for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
                XMLHttpRequest.callbacks[i]( this );
            }
            // call the native send()
            oldSend.apply(this, arguments);
        }
    }
}

// e.g.
addXMLRequestCallback( function( xhr ) {
    console.log( xhr.responseText ); // (an empty string)
});
addXMLRequestCallback( function( xhr ) {
    console.dir( xhr ); // have a look if there is anything useful here
});
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案.如果要获取响应数据,请在状态更改时检查readyState和status.xhr.onreadystatechange = function(){if(xhr.readyState == 4 && xhr.status == 200){console.log(JSON.parse(xhr.responseText)); }} (5认同)
  • console.log(xhr.responseText)是一个空字符串,因为在那一刻xhr是空的.如果将xhr变量传递给全局变量并将延迟设置为几秒钟,则可以直接访问属性 (4认同)
  • @ucheng 如果我们添加 xhr 的 onreadystatechange,它会不会覆盖原来的就绪状态更改方法,最好使用 xhrObj.addEventListener("load", fn) (2认同)

jon*_*ohn 20

既然你提到的jQuery,我知道jQuery提供了一个.ajaxSetup()用于设置全局AJAX选项,其中包括事件触发类似方法success,errorbeforeSend-这是什么听起来像你在找什么.

$.ajaxSetup({
    beforeSend: function() {
        //do stuff before request fires
    }
});
Run Code Online (Sandbox Code Playgroud)

当然,您需要在尝试使用此解决方案的任何页面上验证jQuery可用性.

  • 在今日新闻中:由不使用AJAX的AJAX调用杀死的独角兽 (7认同)
  • 他的意思是jquery AJAX对象.但即便如此,你每次都会使用相同的jquery实例 (3认同)
  • 非常有帮助。想补充一下,另一个触发器是statusCode。通过插入类似 statusCode: { 403: function() { error msg } 的内容,您可以向所有 ajax 函数提供全局身份验证/权限/角色检查,而无需重新编写单个 .ajax 请求。 (2认同)

avi*_*viv 9

有一个技巧可以做到这一点.

在所有脚本运行之前,获取原始XHMHttpReuqest对象并将其保存在不同的var中.然后覆盖原始XMLHttpRequest并通过您自己的对象指向对它的所有调用.

Psuedo代码:

 var savd = XMLHttpRequest;
 XMLHttpRequest.prototype = function() {
         this.init = function() {
         }; // your code
         etc' etc'
 };
Run Code Online (Sandbox Code Playgroud)

  • 这个答案并不完全正确,如果您更改对象的原型,即使已保存的对象也将被更改.此外,整个原型正在被一个功能所取代,这个功能将破坏所有的ajax请求.它确实激励我提供答案. (10认同)

Moh*_*lim 8

我在Github上找到了一个很好的库来完成这项工作,你必须在任何其他js文件之前包含它

https://github.com/jpillora/xhook

这是一个向任何传入响应添加http标头的示例

xhook.after(function(request, response) {
  response.headers['Foo'] = 'Bar';
});
Run Code Online (Sandbox Code Playgroud)

  • 很棒的一个!但即使使用人行横道最新的 chrome 网络视图插件,也无法在 Cordova 应用程序上工作! (2认同)

小智 6

除了 meouw 的答案之外,我还必须将代码注入到拦截 XHR 调用的 iframe 中,并使用上面的答案。然而我不得不改变

XMLHttpRequest.prototype.send = function(){
Run Code Online (Sandbox Code Playgroud)

到:

XMLHttpRequest.prototype.send = function(body)
Run Code Online (Sandbox Code Playgroud)

我必须改变

oldSend.apply(this, arguments);
Run Code Online (Sandbox Code Playgroud)

到:

oldSend.call(this, body);
Run Code Online (Sandbox Code Playgroud)

这是让它在 IE9 和IE8 文档模式下工作所必需的。如果不进行此修改,组件框架(Visual WebGUI)生成的某些回调将不起作用。更多信息请访问这些链接:

如果没有这些修改,AJAX 回发不会终止。


her*_*oin 5

使用"meouw"的答案,如果您想查看请求的结果,我建议使用以下解决方案

function addXMLRequestCallback(callback) {
    var oldSend, i;
    if( XMLHttpRequest.callbacks ) {
        // we've already overridden send() so just add the callback
        XMLHttpRequest.callbacks.push( callback );
    } else {
        // create a callback queue
        XMLHttpRequest.callbacks = [callback];
        // store the native send()
        oldSend = XMLHttpRequest.prototype.send;
        // override the native send()
        XMLHttpRequest.prototype.send = function() {
            // call the native send()
            oldSend.apply(this, arguments);

            this.onreadystatechange = function ( progress ) {
               for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
                    XMLHttpRequest.callbacks[i]( progress );
                }
            };       
        }
    }
}

addXMLRequestCallback( function( progress ) {
    if (typeof progress.srcElement.responseText != 'undefined' &&                        progress.srcElement.responseText != '') {
        console.log( progress.srcElement.responseText.length );
    }
});
Run Code Online (Sandbox Code Playgroud)