web-dev-qa-db-fra.com

Ajouter un "hook" à toutes AJAX requêtes sur une page

J'aimerais savoir s'il est possible de "raccorder" à chaque demande AJAX (soit sur le point d'être envoyé, soit lors d'événements)) et d'effectuer une action. À ce stade, je m en supposant que la page contient d'autres scripts tiers, dont certains peuvent utiliser jQuery, d'autres pas. Est-ce possible?

95
Yuliy

Inspiré par réponse d’aviv , j’ai mené une petite enquête et c’est ce que j’ai trouvé.
Je ne suis pas sûr que ce soit si utile que selon les commentaires du script et bien sûr ne fonctionnera que pour les navigateurs utilisant un objet XMLHttpRequest natif .
Je pense que cela fonctionnera si les bibliothèques javascript sont utilisées, car elles utiliseront l’objet natif si 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
});
107
meouw

REMARQUE: La réponse acceptée ne produit pas la réponse car elle est appelée trop tôt.

Vous pouvez faire cela en interceptant génériquement n'importe quel AJAX globalement et sans foirer les callbacks etc. qui ont peut-être été attribué par un tiers AJAX bibliothèques.

(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);
    };
})();

Quelques autres documents sur ce que vous pouvez faire ici avec l’API addEventListener ici:

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

(Notez que cela ne fonctionne pas <= IE8)

100
John Culviner

Puisque vous parlez de jquery, je sais que jquery propose une méthode .ajaxSetup() qui définit des options ajax globales incluant les déclencheurs d'événements tels que success, error et beforeSend. - ce qui ressemble à ce que vous recherchez.

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

bien entendu, vous devez vérifier la disponibilité de jQuery sur toutes les pages sur lesquelles vous essayez d'utiliser cette solution.

20
jondavidjohn

Il y a une astuce pour le faire.

Avant que tous les scripts soient exécutés, prenez l'objet XHMHttpReuqest d'origine et enregistrez-le dans une autre variable. Puis substituez le XMLHttpRequest d'origine et dirigez tous les appels vers ce dernier via votre propre objet.

Code Psuedo:

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

J'ai trouvé une bonne bibliothèque sur Github qui fait le travail bien, vous devez l'inclure avant tout autre fichier js

https://github.com/jpillora/xhook

voici un exemple qui ajoute un en-tête http à toute réponse entrante

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

En utilisant la réponse de "meouw", je suggère d'utiliser la solution suivante si vous voulez voir les résultats de la requête

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 );
    }
});
5
heroin

jquery ...

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

En plus de la réponse meouw, j'ai dû injecter du code dans un iframe qui intercepte les appels XHR et a utilisé la réponse ci-dessus. Cependant, je devais changer

XMLHttpRequest.prototype.send = function(){

À:

XMLHttpRequest.prototype.send = function(arguments)

Et je devais changer

oldSend.apply(this, arguments);

À:

oldSend.call(this, arguments);

Cela était nécessaire pour que cela fonctionne dans IE9 avec le mode document IE8 . Si cette modification n'était pas apportée, certains rappels générés par la structure de composant (Visual WebGUI) ne fonctionnaient pas. Plus d'infos sur ces liens:

Sans ces modifications AJAX les publications) ne se sont pas terminées.

3
WC1

Ajax-hook est une bibliothèque open source pour accrocher un objet XMLHttpRequest global et modifie la demande et la réponse Ajax par défaut. github: https://github.com/wendux/Ajax-hook , par exemple:

hookAjax({

  //hook callbacks
  onreadystatechange:function(xhr){
   console.log("onreadystatechange called: %O",xhr)
  },

  onload:function(xhr){
   console.log("onload called: %O",xhr)
  },

  //hook function
  open:function(arg,xhr){
   console.log("open called: method:%s,url:%s,async:%s",arg[0],arg[1],arg[2])
  }

})
1
wendu