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

Posted

技术标签:

【中文标题】为页面上的所有 AJAX 请求添加“钩子”【英文标题】:Add a "hook" to all AJAX requests on a page 【发布时间】:2011-07-09 07:21:57 【问题描述】:

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

【问题讨论】:

使用 jQuery 是可能的,所以使用普通的旧 javascript 也是可能的,但每个人都需要至少有 2 个“钩子”。无论如何,为什么要在同一页面上使用两者? 使用这个库怎么样? github.com/slorber/ajax-interceptor 这是一个不错的解决方案:***.com/questions/25335648/… 注意:这个问题的答案不包括在现代浏览器中使用较新的fetch() API 进行的 Ajax 调用。 @nirvana-msu - 也许是这个:github.com/werk85/fetch-intercept/blob/develop/src/index.js 【参考方案1】:

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

您可以这样做,这将在全局范围内拦截任何 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/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress

(注意这不起作用

【讨论】:

谢谢!那是完美的。我只修改了一点:this.status 在这种情况下返回服务器状态200 如果请求正常,则返回 404 如果未找到元素,返回 500 等。但是代码,完美运行。 这可能会让你觉得老了,但你应该得到所有的爱。我被深深地困住了。非常感谢。 只是因为我已经搜索了一下。 "load" 事件仅在成功时调用。如果您不关心结果(只是查询确实结束了),您可以使用"loadend" 事件 花了很多天才找到这个。谢谢你的这个天才。 我已经尝试了很多答案,这与 WKWebView 完美配合。我在使用 Ajax 事件时遇到了麻烦,因为在调用 evaluateJavascript 或使用 addUserScript 之前可能无法加载 jQuery。谢谢!【参考方案2】:

受 aviv's answer 的启发,我做了一些调查,这就是我想出的。我不确定它是否有用,根据脚本中的 cmets当然只适用于使用原生 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
);

【讨论】:

最好扩展这个响应以支持请求后挂钩 根据您的实现,我在 NPM 上发布了一些适用于请求和响应的内容! github.com/slorber/ajax-interceptor console.log( xhr.responseText ) 是一个空字符串,因为此时 xhr 为空。如果您将 xhr 变量传递给全局变量并设置延迟几秒钟,您将能够直接访问属性 不错的答案。如果要获取响应数据,请检查状态变化时的 readyState 和状态。 xhr.onreadystatechange=function() if ( xhr.readyState == 4 && xhr.status == 200 ) console.log(JSON.parse(xhr.responseText)); @ucheng 如果我们加上xhr的onreadystatechange,会不会覆盖原来的ready状态改变方法更好用xhrObj.addEventListener("load", fn)【参考方案3】:

既然你提到了 jquery,我知道 jquery 提供了一个 .ajaxSetup() 方法来设置全局 ajax 选项,其中包括像 successerrorbeforeSend 这样的事件触发器 - 这听起来像你正在寻找的为。

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

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

【讨论】:

感谢您的建议,但不幸的是,这不会拦截未使用​​ AJAX 的 AJAX 调用。 今日新闻:独角兽被不使用 AJAX 的 AJAX 调用杀死 他的意思不是 jquery AJAX 对象。但即便如此,你还是会每次都使用相同的 jquery 实例 非常有帮助。想补充一下,另一个触发器是statusCode。通过插入诸如 statusCode: 403: function() error msg 之类的内容,您可以为所有 ajax 函数提供全局 auth/perms/role 检查,而无需重新编写单个 .ajax 请求。【参考方案4】:

我在 Github 上找到了一个很好的库,它做得很好,你必须在任何其他 js 文件之前包含它

https://github.com/jpillora/xhook

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

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

【讨论】:

太棒了!但是即使使用了人行横道最新的 chrome web-view 插件,也无法在 Cordova 应用程序上工作! 这非常适合我的特殊需求:在 Wordpress 中,从 Events Organizer 插件完整日历中过滤事件 不适用于所有请求,一些 fetch 和 xhr ok,但例如它不能从 google recaptcha 捕获 xhr @SergioQuintero:我认为这是您的 GitHub 问题:github.com/jpillora/xhook/issues/102。考虑在此处删除您的评论,因为这不是图书馆的问题。 @SergioQuintero 任何 XHR 接口的覆盖只发生在该文档中,而不是全局发生在浏览器中,因为这将是一个巨大的安全/隐私漏洞。 reCAPTCHA 在 iframe 中运行,您无法在其中运行覆盖。【参考方案5】:

有一个诀窍。

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

伪代码:

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

【讨论】:

这个答案不太对,如果你改变一个对象的原型,即使保存的原型也会改变。整个原型也被替换为一个会破坏所有 ajax 请求的函数。不过,它确实激发了我提供答案。【参考方案6】:

使用“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 );
    
);

【讨论】:

【参考方案7】:

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

XMLHttpRequest.prototype.send = function()

收件人:

XMLHttpRequest.prototype.send = function(body)

我不得不改变

oldSend.apply(this, arguments);

收件人:

oldSend.call(this, body);

这是让它在具有IE8 文档模式的 IE9 中工作的必要条件。如果不进行此修改,则组件框架(Visual WebGUI)生成的某些回调不起作用。这些链接的更多信息:

Function.prototype.call Function.prototype.apply

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

【讨论】:

【参考方案8】:

jquery...

<script>
   $(document).ajaxSuccess(
        function(event, xhr, settings) 
          alert(xhr.responseText);
        
   );
</script>

【讨论】:

jQuery 不会捕获使用其他库发出的请求。例如 ExtJS。如果您只使用 jQuery,这是一个很好的答案。否则,它不会一直有效。 如果 jquery 实例不同,它甚至不会捕获 jquery 请求。根据需要更改原型

以上是关于为页面上的所有 AJAX 请求添加“钩子”的主要内容,如果未能解决你的问题,请参考以下文章

禁用站点上的所有 AJAX / Websocket 数据请求?

AJAX请求登录页面上的PHP文件

AJAX基本属性

多个页面上的多个 ajax 和表单发布请求

如何使 Rails.ajax(...) 在所有页面上工作?

Ajax技术