Add a "hook" to all AJAX requests on a page

JavascriptAjaxXmlhttprequest

Javascript Problem Overview


I'd like to know if it's possible to "hook" into every single AJAX request (either as it's about to get sent, or on events) and perform an action. At this point I'm assuming that there are other third-party scripts on the page. Some of these might use jQuery, while others do not. Is this possible?

Javascript Solutions


Solution 1 - Javascript

NOTE: The accepted answer does not yield the actual response because it is getting called too early.

You can do this which will generically intercept any AJAX globally and not screw up any callbacks etc. that maybe have been assigned by any third party AJAX libraries.

(function() {
    var origOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
        console.log('request started!');
        this.addEventListener('load', function() {
            console.log('request completed!');
            console.log(this.readyState); //will always be 4 (ajax is completed successfully)
            console.log(this.responseText); //whatever the response was
        });
        origOpen.apply(this, arguments);
    };
})();

Some more docs of what you can do here with the addEventListener API here:

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Monitoring_progress

(Note this doesn't work <= IE8)

Solution 2 - Javascript

Inspired by aviv's answer, I did a little investigating and this is what I came up with.
I'm not sure that it's all that useful as per the comments in the script and of course will only work for browsers using a native XMLHttpRequest object.
I think it will work if javascript libraries are in use as they will use the native object if possible.

function addXMLRequestCallback(callback){
    var oldSend, i;
    if( XMLHttpRequest.callbacks ) {
        // we've already overridden send() so just add the callback
        XMLHttpRequest.callbacks.push( callback );
    } else {
        // create a callback queue
        XMLHttpRequest.callbacks = [callback];
        // store the native send()
        oldSend = XMLHttpRequest.prototype.send;
        // override the native send()
        XMLHttpRequest.prototype.send = function(){
            // process the callback queue
            // the xhr instance is passed into each callback but seems pretty useless
            // you can't tell what its destination is or call abort() without an error
            // so only really good for logging that a request has happened
            // I could be wrong, I hope so...
            // EDIT: I suppose you could override the onreadystatechange handler though
            for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
                XMLHttpRequest.callbacks[i]( this );
            }
            // call the native send()
            oldSend.apply(this, arguments);
        }
    }
}

// e.g.
addXMLRequestCallback( function( xhr ) {
    console.log( xhr.responseText ); // (an empty string)
});
addXMLRequestCallback( function( xhr ) {
    console.dir( xhr ); // have a look if there is anything useful here
});

Solution 3 - Javascript

Since you mention jquery, I know jquery offers a .ajaxSetup() method that sets global ajax options that include the event triggers like success, error, and beforeSend - which is what sounds like what you are looking for.

$.ajaxSetup({
    beforeSend: function() {
        //do stuff before request fires
    }
});

of course you would need to verify jQuery availability on any page you attempt to use this solution on.

Solution 4 - Javascript

I've found a good library on Github that does the job well, you have to include it before any other js files

https://github.com/jpillora/xhook

here is an example that adds an http header to any incoming response

xhook.after(function(request, response) {
  response.headers['Foo'] = 'Bar';
});

Solution 5 - Javascript

There is a trick to do it.

Before all scripts running, take the original XHMHttpReuqest object and save it in a different var. Then override the original XMLHttpRequest and direct all calls to it via your own object.

Psuedo code:

 var savd = XMLHttpRequest;
 XMLHttpRequest.prototype = function() {
     this.init = function() {
     }; // your code
     etc' etc'
 };

Solution 6 - Javascript

Using the answer of "meouw" I suggest to use the following solution if you want to see results of request

function addXMLRequestCallback(callback) {
	var oldSend, i;
	if( XMLHttpRequest.callbacks ) {
		// we've already overridden send() so just add the callback
		XMLHttpRequest.callbacks.push( callback );
	} else {
		// create a callback queue
		XMLHttpRequest.callbacks = [callback];
		// store the native send()
		oldSend = XMLHttpRequest.prototype.send;
		// override the native send()
		XMLHttpRequest.prototype.send = function() {
			// call the native send()
			oldSend.apply(this, arguments);
			
			this.onreadystatechange = function ( progress ) {
			   for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
					XMLHttpRequest.callbacks[i]( progress );
				}
			};       
		}
	}
}

addXMLRequestCallback( function( progress ) {
	if (typeof progress.srcElement.responseText != 'undefined' &&                        progress.srcElement.responseText != '') {
		console.log( progress.srcElement.responseText.length );
	}
});

Solution 7 - Javascript

In addition to meouw's answer, I had to inject code into an iframe which intercepts XHR calls, and used the above answer. However, I had to change

XMLHttpRequest.prototype.send = function(){

To:

XMLHttpRequest.prototype.send = function(body)

And I had to change

oldSend.apply(this, arguments);

To:

oldSend.call(this, body);

This was necessary to get it working in IE9 with IE8 document mode. If this modification was not made, some call-backs generated by the component framework (Visual WebGUI) did not work. More info at these links:

Without these modifications AJAX postbacks did not terminate.

Solution 8 - Javascript

jquery...

<script>
   $(document).ajaxSuccess(
        function(event, xhr, settings){ 
          alert(xhr.responseText);
        }
   );
</script>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYuliyView Question on Stackoverflow
Solution 1 - JavascriptJohn CulvinerView Answer on Stackoverflow
Solution 2 - JavascriptmeouwView Answer on Stackoverflow
Solution 3 - JavascriptjondavidjohnView Answer on Stackoverflow
Solution 4 - JavascriptMohamed AliView Answer on Stackoverflow
Solution 5 - JavascriptavivView Answer on Stackoverflow
Solution 6 - JavascriptheroinView Answer on Stackoverflow
Solution 7 - Javascriptuser1176126View Answer on Stackoverflow
Solution 8 - JavascriptVladimir MiroshnichenkoView Answer on Stackoverflow