上一篇文章介绍了如何监听fetch API 发出的请求。本篇介绍一下如何监听ajax的请求。
监听本质上也是复写。
其中我们主要是覆写 setRequestHeader 方法和send方法,open方法也需要格外注意一点。
因为open、setRequestHeader、send方法都在xhr的原型上,所以这里需要对原型链有一点基本的理解。
另外 我们也需要考虑一点,如果有别的工具对xhr进行了覆写怎么办? 我们再次覆写会不会覆盖他们的?所以我们要考虑尽可能的不对其他业务造成影响。
实现上也很简单,就是覆写前先给存一份。其中要注意的细节和fetch都差不多。我们直接上代码
export function rewriteXHR() {
if (window.location && window.location.protocol === 'file:') {
return
}
try {
// 为了防止其他复写的工具不受影响,这里先把这三个方法存一下
const open = xhr.prototype.open
const send = xhr.prototype.send
const setRequestHeader = xhr.prototype.setRequestHeader
// 复写 open方法
xhr.prototype.open = function(this, method: HttpMethod, url: string) {
// 这里要注意,先执行一下已有的方法
open && open.apply(this, arguments)
this.__startTime = +new Date()
this.method = method
this.url = url
}
xhr.prototype.setRequestHeader = function(this, header: string, value: string) {
// 这里要注意,先执行一下已有的方法
setRequestHeader && setRequestHeader.apply(this, arguments)
try {
if (!this.headers) {
this.headers = {}
}
if (!this.headers[header]) {
this.headers[header] = []
}
if (this.headers[header].push) {
this.headers[header].push(value)
} else {
this.headers[header] = [].concat(this.headers[header], value)
}
} catch {}
}
xhr.prototype.send = function(this, body) {
// 这里要注意,先执行一下已有的方法
send && send.apply(this, arguments)
const ADD_EVENT_LISTENER = 'addEventListener'
const ON_READY_STATE_CHANGE = 'onreadystatechange'
const ReqEndDispatch = (event: Event) => {
try {
const ctx = event.currentTarget
if (!event || !ctx) {
return
}
const contentType = ctx.getResponseHeader('content-type') || ''
if (contentType.toLowerCase().indexOf('application/json') < 0) {
return
}
const response = (function() {
try {
return JSON.parse(ctx.response)
} catch {
return ctx.response
}
})()
const duration = +new Date() - ctx.__startTime
// 这是最终要上报的数据
const finData = {
// 请求响应字段
httpClient: 'XHR',
duration,
referrer: document.referrer,
request: {
url: ctx.url,
method: ctx.method,
params: getUrlParams(ctx.url), // 这些同fetch的复写
data: getReqBodyData(body), // 这些同fetch的复写
headers: ctx.headers,
timeout: ctx.timeout,
},
response: {
data: response,
headers: parseHeaders(ctx.getAllResponseHeaders()), // 这些同fetch的复写
status: ctx.status,
statusText: ctx.statusText,
},
}
// 这里做一下上报
// 。。。。。。
} catch (error) {
console.log('[rewriteXHR.send] request end callback error:', error)
}
}
// 这里做一下监听
if (ADD_EVENT_LISTENER in this) {
this[ADD_EVENT_LISTENER]('load', ReqEndDispatch)
this[ADD_EVENT_LISTENER]('error', ReqEndDispatch)
this[ADD_EVENT_LISTENER]('abort', ReqEndDispatch)
this[ADD_EVENT_LISTENER]('timeout', ReqEndDispatch)
} else {
let userCallback: Function = this[ON_READY_STATE_CHANGE]
// @ts-ignore
this[ON_READY_STATE_CHANGE] = (event: Event) => {
if (this.readyState === 4) {
ReqEndDispatch(event)
}
userCallback && userCallback.apply(this, arguments)
}
}
}
} catch (error) {
console.log('[rewriteXHR] rewrite error:', error)
}
}
两篇文章我们介绍了如何监听请求以及实现方法,通过复写fetch和ajax来实现。
在项目中的实际使用 要将复写方法放到入口处前置执行。这样才能cover住全部的请求。另外,如果有一些白名单的域名不想被监听,在获取数据前应该叫一个过滤,这里就不实现了。
这算是前端监听请求最通俗简单的方法了。当然如果从功能实现上来讲,还是有比较多的方法来实现的。如果在服务器Nginx层去做,或者使用service work去做。
文章评论