Monkey patch XMLHTTPRequest.onreadystatechange
To monkey-patch XMLHttpRequest
s, you need to know how an AJAX request is generally constructed:
- Constructor invocation
- Preparation the request (
setRequestHeader()
,open()
) - Sending the request (
.send
).
General-purpose patch
(function(xhr) { function banana(xhrInstance) { // Example console.log('Monkey RS: ' + xhrInstance.readyState); } // Capture request before any network activity occurs: var send = xhr.send; xhr.send = function(data) { var rsc = this.onreadystatechange; if (rsc) { // "onreadystatechange" exists. Monkey-patch it this.onreadystatechange = function() { banana(this); return rsc.apply(this, arguments); }; } return send.apply(this, arguments); };})(XMLHttpRequest.prototype);
The previous assumed that onreadystatechange
was assigned to the onreadystatechange
handler. For simplicity, I didn't include the code for other events, such as onload
. Also, I did not account for events added using addEventListener
.
The previous patch runs for all requests. But what if you want to limit the patch to a specific request only? A request with a certain URL or async flag and a specific request body?
Conditional monkey-patch
Example: Intercepting all POST
requests whose request body contains "TEST"
(function(xhr) { function banana(xhrInstance) { // Example console.log('Monkey RS: ' + xhrInstance.readyState); } // var open = xhr.open; xhr.open = function(method, url, async) { // Test if method is POST if (/^POST$/i.test(method)) { var send = this.send; this.send = function(data) { // Test if request body contains "TEST" if (typeof data === 'string' && data.indexOf('TEST') >= 0) { var rsc = this.onreadystatechange; if (rsc) { // Apply monkey-patch this.onreadystatechange = function() { banana(this); return rsc.apply(this, arguments); }; } } return send.apply(this, arguments); }; } return open.apply(this, arguments); };})(XMLHttpRequest.prototype);
The main techniques used is the transparent rewrite using...
var original = xhr.method; xhr.method = function(){ /*...*/; return original.apply(this, arguments);};
My examples are very basic, and can be extended to meet your exact wishes. That's up to you, however.
Assuming you can ignore IE...
//Totally untested code, typed at the SO <textarea>... but the concept *should* work, let me know if it doesn't.var OldXMLRequest = XMLHttpRequest;// Create a new instancefunction XMLHttpRequest() { var ajax = new OldXMLRequest(); // save old function var f = ajax.onreadystatechange; ajax.onreadystatechange = function() { console.log("Whatever!"); f(); // Call the old function } return ajax;}
you can learn from Ajax-hook written by chinese!
it is a advanced js to enable Monkey patch XMLHTTPRequest