在页面上的所有AJAX请求中添加一个“钩子”


102

我想知道是否有可能“钩住”每个AJAX请求(即将发送或发生事件)并执行操作。此时,我假设页面上还有其他第三方脚本。其中一些可能使用jQuery,而其他一些则没有。这可能吗?


jQuery有可能,所以普通的旧javascript也有可能,但是您每个人至少要有2个“钩”。无论如何,为什么要在同一页面上同时使用它们?
yoda


这是一个很好的解决方案:stackoverflow.com/questions/25335648/...
phazei

2
注意:这个问题的答案不包括fetch()现在在现代浏览器中通过较新的API 进行的Ajax调用。
jfriend00

1
@ nirvana- msu-
jfriend00

Answers:


109

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
});

扩展此响应以支持请求后挂钩非常好
Sebastien Lorber 2014年

1
根据您的实施,我已经在NPM上发布了一些可以同时处理请求和响应的内容!github.com/slorber/ajax-interceptor
Sebastien Lorber

4
console.log(xhr.responseText)是一个空字符串,因为此时xhr为空。如果您将xhr变量传递给全局变量并设置延迟几秒钟,则可以直接访问属性
工具包

5
好答案。如果要获取响应数据,请在状态更改时检查readyState和状态。xhr.onreadystatechange = function(){如果(xhr.readyState == 4 && xhr.status == 200){console.log(JSON.parse(xhr.responseText)); }
斯坦利·王

1
@ucheng如果我们添加xhr的onreadystatechange,它将更好地覆盖原始的就绪状态更改方法,请使用xhrObj.addEventListener(“ load”,fn)
Mohamed Hussain

125

注意:接受的答案不会产生实际的响应,因为调用时间太早。

您可以执行此操作,这通常会在全局范围内拦截任何 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);
    };
})();

您可以在此处使用addEventListener API做什么的更多文档:

https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress

(请注意,这不起作用== IE8)


谢谢!多数民众赞成在完美。我仅作一点修改:this.status在这种情况下,200如果请求正常,则返回服务器状态;如果未找到该元素,则返回404;否则,返回500。
MrMins

1
这对您来说可能感觉很老,但是您应该为此付出全部的爱。我深陷其中。谢谢你
卡尔斯·阿尔科里亚

仅仅是因为我已经搜索了一下。"load"仅在成功时才调用该事件。如果你不关心结果(只是查询没有结束),您可以使用"loadend"事件
罗穆亚尔德深色

找到了很多天。谢谢您的天才。
戴维(David)

我已经尝试了许多答案,并且可以与WKWebView完美结合。我在使用Ajax事件时遇到了麻烦,因为在我调用EvaluationJavascript或使用addUserScript之前可能未加载jQuery。谢谢!
杰克诚

21

既然你提到的jQuery,我知道jQuery提供了一个.ajaxSetup()方法,其中包括事件触发的套装全局AJAX选项successerror以及beforeSend-这是什么听起来像你在找什么。

$.ajaxSetup({
    beforeSend: function() {
        //do stuff before request fires
    }
});

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


1
感谢您的建议,但是不幸的是,这不会拦截未使用​​AJAX的AJAX调用。
Yuliy 2011年

8
在今日新闻中:独角兽被不使用AJAX的AJAX呼叫杀死
Dashu 2015年

3
他的意思不是jquery AJAX对象。但是即使那样,您还是会每次都使用相同的jquery实例
Shishir Arora 2015年

1
很有帮助。要添加,另一个触发器是statusCode。通过插入类似于statusCode的内容:{403:function(){error msg},您可以向所有ajax函数提供全局auth / perms / role检查,而不必重新编写单个.ajax请求。
Watercayman's

8

有一个技巧。

在运行所有脚本之前,请使用原始XHMHttpReuqest对象并将其保存在其他var中。然后覆盖原始XMLHttpRequest,并通过您自己的对象将所有调用定向到该对象。

伪代码:

 var savd = XMLHttpRequest;
 XMLHttpRequest.prototype = function() {
         this.init = function() {
         }; // your code
         etc' etc'
 };

10
这个答案不太正确,如果您更改对象的原型,甚至保存的对象也将被更改。同样,整个原型也将替换为一个函数,该函数将中断所有ajax请求。它的确激励了我提供答案。
meouw 2011年

8

我在Github上找到了一个很好的库,可以很好地完成工作,您必须在其他任何js文件之前添加它

https://github.com/jpillora/xhook

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

xhook.after(function(request, response) {
  response.headers['Foo'] = 'Bar';
});

2
太好了!但是,即使使用了最新的chrome web-view插件,也无法在Cordova应用程序上工作!
Islam Attrash'Nov

1
这非常适合我的特定需求:在Wordpress中,从Events Organizer插件完整日历中过滤事件
Alfredo Yong

不适用于所有请求,可以进行某些获取和xhr操作,但是例如,它不能从Google Recaptcha中捕获xhr
Sergio Quintero

@SergioQuintero:我认为这是您的GitHub问题:github.com/jpillora/xhook/issues/102。考虑在这里删除您的评论,因为这不是库的问题。
三十

@SergioQuintero XHR接口的任何替代都只会在该文档中发生,而不会在浏览器中全局发生,因为这将是一个巨大的安全/隐私漏洞。reCAPTCHA在iframe中运行,因此您无法在其中运行替换。
Walf

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 );
    }
});


3

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

XMLHttpRequest.prototype.send = function(){

至:

XMLHttpRequest.prototype.send = function(body)

我不得不改变

oldSend.apply(this, arguments);

至:

oldSend.call(this, body);

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.