Я хотел бы захватить содержимое запросов AJAX, используя Greasemonkey.Как перехватить XMLHttpRequests из сценария Greasemonkey?
Кто-нибудь знает, как это сделать?
Я хотел бы захватить содержимое запросов AJAX, используя Greasemonkey.Как перехватить XMLHttpRequests из сценария Greasemonkey?
Кто-нибудь знает, как это сделать?
Не уверен, что вы можете сделать это с помощью greasemonkey, но если вы создадите расширение, вы можете использовать службу наблюдателя и наблюдателя-клиента-наблюдателя.
Как модифицировать XMLHttpRequest.prototype.open или отправить методы с заменами, которые устанавливают свои собственные обратные вызовы и вызывают исходные методы? Обратный вызов может выполнить свою задачу, а затем вызвать обратный вызов с указанным исходным кодом.
Другими словами:
XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open;
var myOpen = function(method, url, async, user, password) {
//do whatever mucking around you want here, e.g.
//changing the onload callback to your own version
//call original
this.realOpen (method, url, async, user, password);
}
//ensure all XMLHttpRequests use our custom open method
XMLHttpRequest.prototype.open = myOpen ;
Вы можете заменить unsafeWindow.XMLHttpRequest объект в документе, с оберткой. Немного кода (не проверено):
var oldFunction = unsafeWindow.XMLHttpRequest;
unsafeWindow.XMLHttpRequest = function() {
alert("Hijacked! XHR was constructed.");
var xhr = oldFunction();
return {
open: function(method, url, async, user, password) {
alert("Hijacked! xhr.open().");
return xhr.open(method, url, async, user, password);
}
// TODO: include other xhr methods and properties
};
};
Но есть одна маленькая проблема: Greasemonkey скрипты выполнить после загрузки страницы, так что страница может использовать или хранить исходный объект XMLHttpRequest во время его последовательность загрузки, поэтому запросов сделанный до выполнения вашего скрипта, или с реальным объектом XMLHttpRequest, не будет отслеживаться вашим скриптом. Я никак не могу обойти это ограничение.
Я написал код для перехвата вызовов ajax при записи прокси-сервера. Он должен работать на большинстве браузеров.
Принятый ответ почти правильно, но он может использовать некоторое улучшение:
(function(open) {
XMLHttpRequest.prototype.open = function() {
this.addEventListener("readystatechange", function() {
console.log(this.readyState);
}, false);
open.apply(this, arguments);
};
})(XMLHttpRequest.prototype.open);
Предпочитают используя применять + аргументы через вызов, потому что тогда вы не имеете явно знать все аргументы, которые даются открытию, которые могут измениться!
Испытано в Chrome 55 и Firefox 50.1.0
В моем случае я хотел изменить responseText, что в Firefox было свойство только для чтения, так что мне пришлось обернуть весь объект XMLHttpRequest. Я не реализовал весь API (в частности, responseType), но он был достаточно хорош, чтобы использовать для всех библиотек, которые у меня есть.
Использование:
XHRProxy.addInterceptor(function(method, url, responseText, status) {
if (url.endsWith('.html') || url.endsWith('.htm')) {
return "<!-- HTML! -->" + responseText;
}
});
Код:
(function(window) {
var OriginalXHR = XMLHttpRequest;
var XHRProxy = function() {
this.xhr = new OriginalXHR();
function delegate(prop) {
Object.defineProperty(this, prop, {
get: function() {
return this.xhr[prop];
},
set: function(value) {
this.xhr.timeout = value;
}
});
}
delegate.call(this, 'timeout');
delegate.call(this, 'responseType');
delegate.call(this, 'withCredentials');
delegate.call(this, 'onerror');
delegate.call(this, 'onabort');
delegate.call(this, 'onloadstart');
delegate.call(this, 'onloadend');
delegate.call(this, 'onprogress');
};
XHRProxy.prototype.open = function(method, url, async, username, password) {
var ctx = this;
function applyInterceptors(src) {
ctx.responseText = ctx.xhr.responseText;
for (var i=0; i < XHRProxy.interceptors.length; i++) {
var applied = XHRProxy.interceptors[i](method, url, ctx.responseText, ctx.xhr.status);
if (applied !== undefined) {
ctx.responseText = applied;
}
}
}
function setProps() {
ctx.readyState = ctx.xhr.readyState;
ctx.responseText = ctx.xhr.responseText;
ctx.responseURL = ctx.xhr.responseURL;
ctx.responseXML = ctx.xhr.responseXML;
ctx.status = ctx.xhr.status;
ctx.statusText = ctx.xhr.statusText;
}
this.xhr.open(method, url, async, username, password);
this.xhr.onload = function(evt) {
if (ctx.onload) {
setProps();
if (ctx.xhr.readyState === 4) {
applyInterceptors();
}
return ctx.onload(evt);
}
};
this.xhr.onreadystatechange = function (evt) {
if (ctx.onreadystatechange) {
setProps();
if (ctx.xhr.readyState === 4) {
applyInterceptors();
}
return ctx.onreadystatechange(evt);
}
};
};
XHRProxy.prototype.addEventListener = function(event, fn) {
return this.xhr.addEventListener(event, fn);
};
XHRProxy.prototype.send = function(data) {
return this.xhr.send(data);
};
XHRProxy.prototype.abort = function() {
return this.xhr.abort();
};
XHRProxy.prototype.getAllResponseHeaders = function() {
return this.xhr.getAllResponseHeaders();
};
XHRProxy.prototype.getResponseHeader = function(header) {
return this.xhr.getResponseHeader(header);
};
XHRProxy.prototype.setRequestHeader = function(header, value) {
return this.xhr.setRequestHeader(header, value);
};
XHRProxy.prototype.overrideMimeType = function(mimetype) {
return this.xhr.overrideMimeType(mimetype);
};
XHRProxy.interceptors = [];
XHRProxy.addInterceptor = function(fn) {
this.interceptors.push(fn);
};
window.XMLHttpRequest = XHRProxy;
})(window);