/**
源码来源:https://github.com/wendux/Ajax-hook
* XHR属性方法:
* Type Function: [abort,getAllResponseHeaders,getResponseHeader,open,overrideMimeType,send,setRequestHeader]
* type object: [onreadystatechange,upload,responseXML,onprogress,onabort,onloadstart,onload,onloadend,onerror,ontimeout]
* type number: [readyState,timeout,UNSENT,OPENED,HEADERS_RECEIVED,LOADING,DONE]
* type string: [statusText,responseType,response,responseText]
*
*/
const ORIGINXHR = '__origin__';
export function hook(proxy: any, win?: typeof globalThis) {
win = win || window;
// 1. 缓存原始的 XMLHttpRequest 对象
let originXhr = win.XMLHttpRequest
// @ts-ignore
win.XMLHttpRequest = function () {
let xhr: any = new originXhr();
for (let attr in xhr) {
// @ts-ignore
let type = typeof xhr[attr];
// 3. 处理方法:函数类型的注入hooks,重写方法,
if (type === "function") {
// console.log("attr==type: function=>", type, attr)
this[attr] = useHookFunction(attr);
}
// 4. 处理属性:非函数类型的添加get set, 忽略保存到原始对象。 this.__origin__.toString() == "[object XMLHttpRequest]"
// this 指向新的XHR对象
else if (attr !== ORIGINXHR) {
// console.log("attr==type: !function=>", type, attr)
Object.defineProperty(this, attr, {
get: getterFactory(attr),
set: setterFactory(attr),
enumerable: true // 确保能被for in 循环
})
}
}
this[ORIGINXHR] = xhr;
// console.log(this, "hook XMLHttpRequest")
}
function useHookFunction(fun: string) {
return function () {
let args = [].slice.call(arguments);
if (proxy[fun]) {
let ret = proxy[fun].call(this, args, this[ORIGINXHR])
if (ret) return ret;
}
return this[ORIGINXHR][fun].apply(this[ORIGINXHR], args);
}
}
function getterFactory(attr: any) {
return function () {
let v = this.hasOwnProperty(attr + "_") ? this[attr + "_"] : this[ORIGINXHR][attr];
return v;
// let attrGetterHook = (proxy[attr] || {})["getter"];
// return attrGetterHook && attrGetterHook(v, this) || v;
}
}
function setterFactory(attr: string) {
console.log("all == attr", attr)
return function (v: any) {
let xhr = this[ORIGINXHR]; // 原始 XHR
let that = this; // 新 XHR
let hook = proxy[attr]; // 传入的 hook func
// 1. 处理的事件: onloadend,onerror,ontimeout
if (attr.startsWith('on')) {
console.log("on == attr", attr)
that[attr + "_"] = v;
console.log("setterFactory", v, proxy[attr])
xhr[attr] = function (e: any) {
e = configEvent(e, that)
let ret = proxy[attr] && proxy[attr].call(that, xhr, e) // 1. 原始XHR上执行传入的hook事件(方法)
console.log("ret", ret)
ret || v.call(that, e); // 2.新的XHR上执行外部定义的事件回调
}
}
// 2. 处理其他属性
else {
// 普通属性拦截器写法
// hook(
// //需要拦截的属性名
// timeout: {
// //拦截写操作
// setter: function (v, xhr) {
// //超时最短为1s,返回值为最终值。
// return Math.max(v, 1000);
// }
// }
// )
// let attrSetterHook = (hook || {})["setter"]; // 源码这个写法有问题,修改为如下:
let attrSetterHook = hook["setter"] || null;
console.log("un event:", attr, attrSetterHook)
v = attrSetterHook && attrSetterHook(v, that) || v // 替换普通属性为拦截器的值
this[attr + "_"] = v; // 新的xhr对象上复制属性
try {
xhr[attr] = v; // 原始xhr对象赋值属性
} catch (e) {
}
}
}
}
function unHook() {
win!.XMLHttpRequest = originXhr;
// @ts-ignore
originXhr = undefined;
}
return {
originXhr, unHook
}
}
export function configEvent(event:Event, xhrProxy: any) {
let e: any = {};
for (let attr in event) {
// @ts-ignore
e[attr] = event[attr];
}
e.target = e.currentTarget = xhrProxy
return e;
}