Monkey patch XMLHTTPRequest.onreadystatechange
Asked Answered
T

3

8

How would go about monkey patching the XMLHTTPRequest's onreadystatechange function. I'm trying to add a function that would be called when every ajax request made from a page come back.

I know this sounds like a terrible idea, but the use case is quite peculiar. I want to use a certain SDK with a console (jqconsole) but show status and results from ajax calls within the console without modifying the external SDK.

I've looked at this post which had great info, but nothing on monkey patching the callback which seem to exceed my JavaScript skills.

P.S Can't use jQuery since it only supports ajax calls made from jQuery not from XMLHTTPRequests directly which is the case here.

Tiber answered 25/9, 2012 at 2:46 Comment(0)
C
15

To monkey-patch XMLHttpRequests, you need to know how an AJAX request is generally constructed:

  1. Constructor invocation
  2. Preparation the request (setRequestHeader(), open())
  3. 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.

Certiorari answered 25/9, 2012 at 8:31 Comment(0)
J
0

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 instance
function 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;
}
Janie answered 25/9, 2012 at 4:39 Comment(1)
1. When the XHR object is constructed, there's certainly no readystatechange event handler. 2. When calling the method using f(), the context is lost: this should be the xhr instance, not window or undefined.Certiorari
A
0

you can learn from Ajax-hook written by chinese!

it is a advanced js to enable Monkey patch XMLHTTPRequest

Adham answered 3/6, 2017 at 14:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.