Aggiungi un "hook" a tutte le richieste AJAX su una pagina


102

Mi piacerebbe sapere se è possibile "agganciarsi" a ogni singola richiesta AJAX (sia quando sta per essere inviata, sia su eventi) ed eseguire un'azione. A questo punto presumo che nella pagina siano presenti altri script di terze parti. Alcuni di questi potrebbero utilizzare jQuery, mentre altri no. È possibile?


È possibile con jQuery, quindi è possibile con il semplice vecchio javascript, ma dovresti avere almeno 2 "hook" per ciascuno di essi. Comunque, perché usarli entrambi sulla stessa pagina?
yoda

2
Che ne dici di usare questa libreria? github.com/slorber/ajax-interceptor
Sebastien Lorber

Questa è una bella soluzione: stackoverflow.com/questions/25335648/...
phazei

2
Nota: le risposte a questa domanda non coprono le chiamate Ajax effettuate dalla nuova fetch()API ora nei browser moderni.
jfriend00

Risposte:


109

Ispirato dalla risposta di aviv , ho indagato un po 'e questo è ciò che mi è venuto in mente.
Non sono sicuro che sia così utile secondo i commenti nello script e ovviamente funzionerà solo per i browser che utilizzano un oggetto XMLHttpRequest nativo .
Penso che funzionerà se le librerie javascript sono in uso poiché useranno l'oggetto nativo, se possibile.

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

sarebbe bello estendere questa risposta per supportare gli hook post-richiesta
Sebastien Lorber

1
In base alla tua implementazione, ho pubblicato qualcosa su NPM che funziona sia con le richieste che con le risposte! github.com/slorber/ajax-interceptor
Sebastien Lorber

4
console.log (xhr.responseText) è una stringa vuota perché in quel momento xhr è vuoto. se passi la variabile xhr alla variabile globale e imposti un ritardo di pochi secondi, sarai in grado di accedere direttamente alle proprietà
Toolkit

5
bella risposta. Se si desidera ottenere i dati di risposta, controllare readyState e lo stato quando lo stato è cambiato. xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {console.log (JSON.parse (xhr.responseText)); }}
Stanley Wang

1
@ucheng Se aggiungiamo onreadystatechange di xhr, sovrascriverà il metodo di modifica dello stato ready originale, meglio utilizzare xhrObj.addEventListener ("load", fn)
Mohamed Hussain

125

NOTA: la risposta accettata non fornisce la risposta effettiva perché viene chiamata troppo presto.

Puoi fare ciò che intercetterà genericamente qualsiasi AJAX a livello globale e non rovinerà alcun callback ecc. Che potrebbe essere stato assegnato da librerie AJAX di terze parti.

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

Alcuni altri documenti su ciò che puoi fare qui con l'API addEventListener qui:

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

(Nota che non funziona <= IE8)


Grazie! Funziona perfettamente. Modifico solo un po ': this.statusrestituisco lo stato del server in questo caso 200se la richiesta è OK, 404 se l'elemento non è stato trovato, 500, ecc. Ma il codice funziona perfettamente.
MrMins

1
Questo può sembrare vecchio per te, ma meriti tutto l'amore per questo. Ero profondamente bloccato. Grazie mille.
Carles Alcolea

Semplicemente perché ho cercato un po 'per questo. L' "load"evento è chiamato solo in caso di successo. Se non ti interessa il risultato (solo che la query è terminata) puoi utilizzare l' "loadend"evento
Romuald Brunet

Ci sono voluti molti giorni per trovarlo. Grazie per questo pezzo di genio.
David

Ho provato molte risposte e funziona perfettamente con WKWebView. Ho avuto problemi a utilizzare gli eventi Ajax poiché jQuery potrebbe non essere caricato prima di chiamare assessJavascript o utilizzare addUserScript. Grazie!
Jake Cheng,

21

Dal momento che si parla di jQuery, lo so offerte jQuery un .ajaxSetup()metodo che imposta le opzioni Ajax globali che includono i trigger evento come success, errore beforeSend- che è quello che suona come quello che stai cercando.

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

ovviamente dovrai verificare la disponibilità di jQuery su qualsiasi pagina su cui tenti di utilizzare questa soluzione.


1
Grazie per il suggerimento, ma questo purtroppo non intercetta le chiamate AJAX che vengono fatte non utilizzando AJAX.
Yuliy

8
Nelle notizie di oggi: Unicorni uccisi da chiamate AJAX che vengono effettuate senza utilizzare AJAX
Dashu

3
non intendeva oggetto jquery AJAX. Ma anche in questo caso potrai usare la stessa istanza jquery ogni volta
Shishir Arora

1
Estremamente utile. Voleva aggiungere, un altro trigger è statusCode. Collegando qualcosa come statusCode: {403: function () {error msg} puoi fornire un controllo globale di autenticazione / perms / ruolo a tutte le funzioni ajax senza dover riscrivere una singola richiesta .ajax.
Watercayman

8

C'è un trucco per farlo.

Prima che tutti gli script vengano eseguiti, prendi l'oggetto XHMHttpReuqest originale e salvalo in una diversa var. Quindi sovrascrivi l'originale XMLHttpRequest e indirizza tutte le chiamate ad esso tramite il tuo oggetto.

Codice Psuedo:

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

10
Questa risposta non è del tutto corretta, se modifichi il prototipo di un oggetto verrà modificato anche quello salvato. Inoltre l'intero prototipo viene sostituito con una funzione che interromperà tutte le richieste ajax. Tuttavia, mi ha ispirato a offrire una risposta.
meouw

8

Ho trovato una buona libreria su GitHub che fa bene il lavoro, devi includerla prima di qualsiasi altro file js

https://github.com/jpillora/xhook

ecco un esempio che aggiunge un'intestazione http a qualsiasi risposta in arrivo

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

2
Grande! Ma non ha funzionato sull'applicazione Cordova nemmeno con l'ultimo plug-in di visualizzazione Web di Chrome!
Islam Attrash

1
Questo ha funzionato perfettamente per le mie esigenze particolari: in Wordpress, filtraggio degli eventi dal calendario completo del plug-in di Events Organizer
Alfredo Yong

Non funziona per tutte le richieste, alcuni fetch e xhr ok, ma per esempio non cattura xhr da google recaptcha
Sergio Quintero

@SergioQuintero: presumo che questo sia il tuo problema su GitHub: github.com/jpillora/xhook/issues/102 . Considera l'idea di eliminare il tuo commento qui, poiché non è stato un problema con la libreria.
trenta punti

@SergioQuintero Qualsiasi override delle interfacce XHR avviene solo in quel documento, non globalmente nel browser perché sarebbe un enorme buco di sicurezza / privacy. reCAPTCHA viene eseguito in un iframe e non è possibile eseguire l'override lì.
Walf

5

Utilizzando la risposta di "meouw" suggerisco di utilizzare la seguente soluzione se vuoi vedere i risultati della richiesta

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

3

jquery ...

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

1
jQuery non rileverà le richieste effettuate utilizzando altre librerie. Ad esempio ExtJS. Se stai usando solo jQuery, è una buona risposta. Altrimenti, non funzionerà sempre.
npiani

1
non cattura nemmeno le richieste jquery se l'istanza jquery è diversa. Cambio di prototipo se necessario
Shishir Arora

3

Oltre alla risposta di meouw, ho dovuto iniettare codice in un iframe che intercetta le chiamate XHR e ho utilizzato la risposta sopra. Tuttavia, ho dovuto cambiare

XMLHttpRequest.prototype.send = function(){

Per:

XMLHttpRequest.prototype.send = function(body)

E ho dovuto cambiare

oldSend.apply(this, arguments);

Per:

oldSend.call(this, body);

Ciò era necessario per farlo funzionare in IE9 con la modalità documento IE8 . Se questa modifica non è stata apportata, alcuni call-back generati dal framework dei componenti (Visual WebGUI) non hanno funzionato. Maggiori info a questi link:

Senza queste modifiche, i postback AJAX non venivano interrotti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.