request-monitor
Version:
304 lines (271 loc) • 7.79 kB
JavaScript
/**
*
* @param {*} listener
*
http Info: {
__type: "fetch", // 使用底层库类型,有 fetch 和 xhr
url: "",
method: "",
timeout: 0, // 超时时间
params: {}, // 请求参数
responseStatus: 200, // 如果是 -1 ,代表网络异常
responseStatusText: "ok",
responseJson: {"code": 0},
responseText: "{"code": 0}",
requestTime: 100, 请求时间,单位为 ms
}
*/
const listeners = {};
let id = 1;
function getId () {
return id++;
}
function handleEndTime(info){
let endTime = new Date ().getTime ();
info.requestTime = endTime - info.startTime;
info.endTime = endTime;
}
function handleDefaultApi (emit) {
if (window._requestMonitorIsLoad) return null;
window._requestMonitorIsLoad = true;
handleXhr (emit);
handleFetch (emit);
return true;
}
function handleHeader(xhr) {
try{
var headers = xhr.getAllResponseHeaders();
if (!headers) return;
if (typeof headers === 'object') {
return headers;
}
var newHeaders = {}, headers = headers.split(/[\r\n]/).forEach(function (header) {
var index = header.indexOf(":");
var name = header.substr(0, index);
var value = header.substr(index + 2);
if (name) {
newHeaders[name] = value;
}
})
return newHeaders;
}catch(e){
return {}
}
}
function handleXhr (emit) {
let _open = XMLHttpRequest.prototype.open;
let _send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (...args) {
const method = args[0];
const url = args[1];
this._monitor = {
__type: 'xhr',
};
_open.apply (this, args);
Object.assign (this._monitor, {
method,
url,
});
};
XMLHttpRequest.prototype.send = function (...args) {
_send.apply (this, args);
this._monitor.startTime = new Date ().getTime ();
this._monitor.timeout = this.timeout || 0;
try {
if (args.length > 0 && typeof args[0] === 'string') {
this._monitor.params = args[0];
}
} catch (err) {}
this.addEventListener (
'load',
function () {
let contentType = this.getResponseHeader (
'Content-Type'
);
contentType = contentType || '';
contentType = contentType.toLowerCase ();
const ignores = ['blob', 'arraybuffer', 'moz-chunked-arraybuffer', "ms-stream"];
if(this.responseType && ignores.indexOf(this.responseType) > -1){
return; // ignore blob type
}
if (contentType.indexOf ('application/json') !== -1) {
this._monitor.responseText = this.responseText;
try{
this._monitor.responseJson = JSON.parse (this.responseText);
}catch(e){}
} else if (contentType.indexOf ('text/plain') !== -1) {
this._monitor.responseText = this.responseText;
}
this._monitor.responseHeaders = handleHeader(this);
this._monitor.responseStatus = this.status;
this._monitor.responseStatusText = this.statusText;
emit (this._monitor);
},
false
);
this.addEventListener ('error', () => {
this._monitor.responseStatusText = 'Xhr Network request exception';
this._monitor.responseStatus = -1;
emit (this._monitor);
});
this.addEventListener ('timeout', () => {
emit (this._monitor);
});
};
}
function getParams (options) {
let params;
try {
if (options.body && typeof options.body === 'string') {
params = options.body;
}
} catch (er) {}
return params;
}
function handleFetch (emit) {
if (window.fetch) {
const _Promise = window.fetch.Promise || Promise;
const _fetch = window.fetch;
function getHeaders(response){
try{
let headers = response.headers.entries();
let result = {};
for (var pair of headers) {
result[pair[0]] = pair[1];
}
return result;
}catch(e){
console.warn(e);
return {};
}
}
function monitorFetch (url, options = {}) {
const params = getParams (options);
const _monitor = {
__type: 'fetch',
url,
method: options.method || 'GET',
params,
startTime: new Date ().getTime (),
timeout: options.timeout || 0
};
const fetchRequest =
// _Promise
// .race ([
// new _Promise ((resolve, reject) => {
// setTimeout (() => {
// let err = new Error ('前端fetch Network request timeout: ' + options.timeout);
// err.status = 504;
// reject (err);
// }, timeout);
// }),
// ])
_fetch (url, options)
.then (response => {
let _text = response.text;
_monitor.responseStatus = response.status;
_monitor.responseHeaders = getHeaders(response)
_monitor.responseStatusText = response.statusText;
response.json = () => {
return new _Promise ((resolve, reject) => {
_text
.call (response)
.then (text => {
try {
let json = JSON.parse (text);
_monitor.responseText = text;
_monitor.responseJson = json;
emit (_monitor);
resolve (json);
} catch (e) {
e.type = 'invalid-json';
_monitor.responseText = text;
emit(_monitor);
reject (e);
}
})
.catch (err => {
reject (err);
});
});
};
response.text = () => {
return new _Promise ((resolve, reject) => {
_text
.call (response)
.then (text => {
_monitor.responseText = text;
emit (_monitor);
resolve (text);
})
.catch (err => {
reject (err);
});
});
};
return response;
});
return new _Promise ((resolve, reject) => {
fetchRequest
.then (data => {
resolve (data);
})
.catch (err => {
_monitor.responseStatus = err.status || -1;
_monitor.responseText = err.message || 'Fetch Network request exception';
emit (_monitor);
reject (err);
});
return fetchRequest;
});
}
monitorFetch.Promise = _Promise;
Object.defineProperty (monitorFetch, 'Promise', {
set (value) {
// monitorFetch.Promise = value;
_fetch.Promise = value;
},
get () {
return _fetch.Promise;
},
});
window.fetch = monitorFetch;
}
}
function emit (info) {
handleEndTime(info)
try{
Object.keys (info).forEach (key => {
if (typeof info[key] === 'undefined') {
delete info[key];
}
});
Object.keys (listeners).forEach (key => {
setTimeout(()=> {
try{
listeners[key] (info)
}catch(err){
console.error(err)
}
})
});
}catch(e){
console.error(e)
}
}
module.exports = requestMonitor;
function requestMonitor (listener) {
listener = listener || (httpInfo => httpInfo);
if (typeof listener !== 'function') {
throw new Error (`The listener Type must be function`);
}
let _id = getId ();
listeners[_id] = listener;
handleDefaultApi (emit);
return {
cancel: () => {
delete listeners[_id];
},
};
}
requestMonitor.handleDefaultApi = handleDefaultApi;