如何拦截不同 JS 库发出的所有 AJAX 请求

我正在用不同的 JS 库(AngularJS,OpenLayers,...)构建一个 web 应用程序,需要一种方法来拦截所有的 AJAX 响应,以便在登录用户会话过期(响应返回 401 Unauthorized状态)时能够将他重定向到登录页面。

我知道 AngularJS 提供了 interceptors来管理这样的场景,但是无法找到一种方法来实现 OpenLayers 请求中的这种注入。所以我选择了一种普通的 JS 方法。

在这里 我发现了这段代码..。

(function(open) {


XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {


this.addEventListener("readystatechange", function() {
console.log(this.readyState); // this one I changed
}, false);


open.call(this, method, url, async, user, pass);
};


})(XMLHttpRequest.prototype.open);

... 这是我改编的,看起来和预期的一样(只在上一次 Google Chrome 上测试过)。

当它修改 XMLHTTPRequest 的原型时,我想知道这会导致多大的危险,或者是否会产生严重的性能问题。顺便问一下,还有其他有效的选择吗?

更新: 如何在发送请求之前拦截它们

之前的把戏还行。但是,如果在同一场景中,您希望在发送请求之前注入一些头文件,该怎么办呢?做以下事情:

(function(send) {


XMLHttpRequest.prototype.send = function(data) {


// in this case I'm injecting an access token (eg. accessToken) in the request headers before it gets sent
if(accessToken) this.setRequestHeader('x-access-token', accessToken);


send.call(this, data);
};


})(XMLHttpRequest.prototype.send);
59132 次浏览

对于某些版本的 IE(9及以下),这不会捕获 XMLHttpRequest。根据库的不同,他们可能首先查找 IE 的专有 ActiveX 控件。

当然,如果您在 IE 下使用非严格的 DOCTYPE,那么所有的赌注都将取消,但是我相信您知道这一点。

参考资料: 我能

这种类型的函数挂钩是完全安全的,并且由于其他原因,定期在其他方法上执行。

而且,对性能的唯一影响实际上是每个 .open()只有一个额外的函数调用,再加上您自己执行的任何代码,当涉及到网络调用时,这可能是无关紧要的。


在 IE 中,这不会捕获任何试图使用 ActiveXObject控制方法执行 Ajax 的代码。编写良好的代码首先查找 XMLHttpRequest对象,如果可用,则使用该对象,该对象自 IE7以来一直可用。但是,可能有一些代码使用 ActiveXObject方法,如果它是可用的,这将是真实的,通过 IE 的后期版本。


在现代浏览器中,还有其他发出 Ajax 调用的方法,比如 fetch()接口,所以如果想要钩住所有 Ajax 调用,你必须钩住的不仅仅是 XMLHttpRequest

正如 Firefox AMO 编辑器 Rob W所指出的,

下面的代码更改 XMLHttpRequest 的行为, 如果未指定第三个(“异步”)参数,则默认值为 true. When it is specified and undefined, it is equivalent to "false", 转换同步 HTTP 请求中的请求 在处理请求时阻塞 UI,以及 XMLHttpRequestAPI 也被禁用。

...

要解决这个问题,请将 open.call (... .)替换为 open.application (this,Senate) ;

And here is a reference link:

Https://xhr.spec.whatwg.org/#the-open ()-方法

试试这个

let oldXHROpen = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function(method, url, async, user, password) {


console.log({method});


// Show loader


this.addEventListener('load', function() {
console.log('load: ' + this.responseText);
// Hide loader
});
           

return oldXHROpen.apply(this, arguments);
}