前端监控数据收集(请求拦截)

12次阅读

共计 2761 个字符,预计需要花费 7 分钟才能阅读完成。

所谓 web, 即使你我素未谋面,便知志趣相投;足不出户,亦知世界之大。
01 — 为什么拦截请求
现在的 web 应用,大都是通过请求(http)去获取资源,拿到资源后再呈现给用户,一个页面中可以有多个这样的请求。每一次请求的开始,等待,完成,异常都会有相应的状态来标识。我们在自己的框架中通常都会使用一个全局过滤器,来拦截请求,目的大同小异:

在发送请求之前,修改请求参数,添加请求头
请求发送中的进度计算(通常是文件上传)
请求出错后的捕获
请求结束后,处理后台返回数据结构,进行适配
……

看看请求的整个流程图:

而我们最常用的发送请求的便是 XMLHttpRequest。
XMLHttpRequest.readyState 的五种就绪状态:

0:请求未初始化(还没有调用 open())。
1:请求已经建立,但是还没有发送(还没有调用 send())。
2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
3:请求在处理中;通常响应中已有部分数据可用了,但是服务器还没有完成响应的生成。
4:响应已完成;您可以获取并使用服务器的响应了。

并且 XMLHttpRequest 还提供了每个阶段的事件:

abort 如果请求中止,会触发 abort 事件。

error 网络错误(如太多重定向)会阻止请求完成,会触发 error 事件。

load 当事件完成,会触发 load 事件。

loadend 当一个请求完成,无论成功(load)或者不成功(abort/error)后触发

loadstart 当调用 send() 时,触发单个 loadstart 事件。

progress 当等待服务器的响应时,XHR 对象会发生 progress 事件。通常每隔 50 毫秒左右,所以可以使用这事件给用户反馈请求的进度。

timeout 当等待服务器的响应超时会触发。

02 — 如何拦截请求
了解了 XMLHttpRequest 的请求流程后,我们就可以开始去拦截浏览器发出的请求,去做我们想做的事。
方式一:
(function (xhr) {
// Capture request before any network activity occurs:
var send = xhr.send;
xhr.send = function (data) {
this.addEventListener(‘loadstart’, onLoadStart);
this.addEventListener(‘loadend’, onLoadEnd);
this.addEventListener(‘error’, onError);
return send.apply(this, arguments);
};
})(XMLHttpRequest.prototype);

这种是最简单直接的方式,修改 XMLHttpRequest 的原型,在发送请求时开启事件监听。大多数情况下都是没什么大问题的,但后来发现在 Angular4+ 以上版本中这样去拦截,请求触发 loadend 事件后获取到的请求响应成功与否状态始终为 false,因为 Angualr2 后来的版本也使用事件监听来处理拦截,有些地方就冲突了。
方式二:
出现问题总要解决吧,然后就采用方法一的升级版本,完全重写 XMLHttpRequest。
(function () {
// create XMLHttpRequest proxy object
var oldXMLHttpRequest = XMLHttpRequest;
// define constructor for my proxy object
window.XMLHttpRequest = function () {
var actual = new oldXMLHttpRequest();
var self = this;
this.onreadystatechange = null;
// this is the actual handler on the real XMLHttpRequest object
actual.onreadystatechange = function () {
if (this.readyState == 1) {
onLoadStart.call(this);
} else if (this.readyState == 4) {
if(this.status==200)
onLoadEnd.call(this);
else{
onError.call(this);
}
}
if (self.onreadystatechange) {
return self.onreadystatechange();
}
};


// add all proxy getters
[“status”, “statusText”, “responseType”, “response”,
“readyState”, “responseXML”, “upload”
].forEach(function (item) {
Object.defineProperty(self, item, {
get: function () {
return actual[item];
},
set: function (val) {
actual[item] = val;
}
});
});


// add all proxy getters/setters
[“ontimeout, timeout”, “withCredentials”, “onload”, “onerror”, “onprogress”].forEach(function (item) {
Object.defineProperty(self, item, {
get: function () {
return actual[item];
},
set: function (val) {
actual[item] = val;
}
});
});


// add all pure proxy pass-through methods
[“addEventListener”, “send”, “open”, “abort”, “getAllResponseHeaders”,
“getResponseHeader”, “overrideMimeType”, “setRequestHeader”, “removeEventListener”
].forEach(function (item) {
Object.defineProperty(self, item, {
value: function () {
return actual[item].apply(actual, arguments);
}
});
});
}
})();

03 — 项目实战
现在我们可以放心的拦截浏览器发出的请求了,妈妈再也不用担心我的学习了,哈哈。说一千道一万,来点干货,直接看项目。
传送门:web-monitor

喜欢请点个赞呗
或者去 https://github.com/kisslove/w… Star 一下
或者打赏一下
再或者……
哈哈,想法有点多了。

正文完
 0