Додайте "гачок" до всіх запитів AJAX на сторінці


103

Мені хотілося б знати, чи можна "підключити" кожен запит AJAX (або як він збирається відправити, або на події) та виконати дію. На даний момент я припускаю, що на сторінці є й інші сторонні сценарії. Деякі з них можуть використовувати jQuery, а інші -. Чи можливо це?


Це можливо з jQuery, так що це можливо із звичайним старим JavaScript, але вам потрібно мати принаймні 2 "гачки" для кожного з них. У будь-якому випадку, навіщо використовувати обидва на одній сторінці?
— йода

2
Як щодо використання цієї бібліотеки? github.com/slorber/ajax-interceptor
— Себастьян Лорбер

Це хороше рішення: stackoverflow.com/questions/25335648 / ...
— phazei

2
Примітка. Відповіді на це запитання не охоплюють дзвінки Ajax, здійснені за допомогою нової fetch()API тепер у сучасних браузерах.
— jfriend00

Відповіді:


109

Надихнувшись відповіді Авіва , я трохи розслідував, і ось що я придумав.
Я не впевнений, що це все так корисно, як за коментарями до сценарію, і, звичайно, буде працювати лише для браузерів, що використовують нативний об’єкт XMLHttpRequest .
Я думаю, що це спрацює, якщо використовуються бібліотеки javascript, оскільки вони, можливо, використовуватимуть нативний об’єкт.

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

було б непогано поширити цю відповідь на підтримку гаків після запиту
— Sebastien Lorber,

1
На основі вашої реалізації я опублікував щось на NPM, що працює як із запитами, так і з відповідями! github.com/slorber/ajax-interceptor
— Себастьян Лорбер

4
console.log (xhr.responseText) - порожній рядок, оскільки в цей момент xhr порожній. якщо ви передасте змінну xhr до глобальної змінної та встановите затримку на кілька секунд, ви зможете отримати доступ до властивостей безпосередньо
— Toolkit

5
приємна відповідь. Якщо ви хочете отримати дані відповіді, перевірте стан готовності та стан, коли стан змінився. xhr.onreadystatechange = function () {if (xhr.readyState == 4 && xhr.status == 200) {console.log (JSON.parse (xhr.responseText)); }}
— Стенлі Ван

1
@ucheng Якщо ми додамо onreadystatechange xhr, чи замінить оригінальний метод зміни стану готового кращого використання xhrObj.addEventListener ("load", fn)
— Мохамед Хуссейн

126

ПРИМІТКА. Прийнята відповідь не дає фактичної відповіді, оскільки її називають занадто рано.

Ви можете зробити це, що загалом перехопить будь-який AJAX у всьому світі і не викрутить будь-які зворотні дзвінки тощо, які, можливо, були призначені будь-якими сторонніми бібліотеками AJAX.

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

Ще кілька документів про те, що ви можете зробити тут з API addEventListener тут:

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

(Зверніть увагу, це не працює <= IE8)


Дякую! Це працює чудово. Я лише трохи змінюю: this.statusповерніть статус сервера в цьому випадку, 200якщо запит буде в порядку, 404, якщо елемент не знайдено, 500 і т.д.
— MrMins

1
Це може здатися вам старим, але ви заслужили на це всю любов. Я глибоко застряг. Дякую купу.
— Carles Alcolea

Просто тому, що я трохи шукав цього. "load"Подія викликається тільки на успіх. Якщо ви не переймаєтесь результатом (тільки щоб запит закінчився), ви можете скористатись "loadend"подією
— Romuald Brunet

Щоб знайти це, минуло багато днів. Дякую за цей геніальний шматок.
— Девід

Я спробував багато відповідей, і це прекрасно працює з WKWebView. У мене виникли проблеми з використанням подій Ajax, оскільки jQuery може не завантажуватися до того, як я дзвоню оценку JavaScript або використовувати addUserScript Дякую!
— Джейк Ченг

21

Так як ви говорите JQuery, я знаю , що Jquery пропонує великий .ajaxSetup()метод , який встановлює глобальні параметри Ajax , які включають тригери подій , як success, errorі beforeSend- що то , що звучить як то , що ви шукаєте.

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

звичайно, вам потрібно буде перевірити наявність jQuery на будь-якій сторінці, на якій ви намагаєтесь використовувати це рішення.


1
Дякуємо за пропозицію, але це, на жаль, не перехоплює дзвінки AJAX, які виконуються без використання AJAX.
— Юлій

8
У сьогоднішніх новинах: Єдинороги, вбиті дзвінками AJAX, які виконуються не за допомогою AJAX
— Дашу

3
він не означає jquery AJAX об'єкт. Але навіть тоді ви
— будете

1
Дуже корисно. Хотіли додати, ще одним тригером є statusCode. Підключивши щось на зразок statusCode: {403: function () {error msg} ви можете надати глобальну перевірку auth / perms / role для всіх функцій ajax без необхідності повторного запису жодного запиту .ajax.
— Водяний кайман

8

Для цього є хитрість.

Перш ніж всі сценарії запущені, візьміть оригінальний об’єкт XHMHttpReuqest і збережіть його в іншому var. Потім замініть оригінальний XMLHttpRequest і направляйте всі дзвінки на нього через власний об’єкт.

Код Псуедо:

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

10
Ця відповідь не зовсім правильна, якщо змінити прототип об'єкта, навіть збережений буде змінено. Також весь прототип замінюється однією функцією, яка порушить всі запити на ajax. Це все ж надихало мене запропонувати відповідь.
— meouw

8

Я знайшов хорошу бібліотеку в Github, яка добре виконує цю роботу, ви повинні включити її до будь-яких інших js-файлів

https://github.com/jpillora/xhook

ось приклад, який додає заголовка http до будь-якої вхідної відповіді

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

2
Чудовий! Але не працювало над додатком Кордова навіть із останнім хромованим плагіном для веб-перегляду на кросс-хові!
— Іслам Атрашш

1
Це спрацювало ідеально для моїх потреб: У Wordpress фільтрування подій з повного календаря плагіна для Організатора подій
— Альфредо Йонг

Працює не для всіх запитів, якийсь збір і xhr добре, але, наприклад, він не ловить xhr з google recaptcha
— Sergio Quintero

@SergioQuintero: Я припускаю, що це ваша проблема GitHub: github.com/jpillora/xhook/isissue/102 . Спробуйте видалити тут свій коментар, оскільки це не було проблемою з бібліотекою.
— тридцятиріччя

@SergioQuintero Будь-яке переопрацювання XHR-інтерфейсів відбувається лише в тому документі, а не в усьому світі в браузері, оскільки це було б великою діркою безпеки / конфіденційності. reCAPTCHA запускається в iframe, і ви не можете запустити заміщення там.
— Walf

5

Використовуючи відповідь "meouw", я пропоную скористатися наступним рішенням, якщо ви хочете побачити результати запиту

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 не буде вловлювати запити, зроблені за допомогою інших бібліотек. Наприклад, ExtJS. Якщо ви використовуєте лише jQuery, це хороша відповідь. Інакше він не працюватиме весь час.
— npiani

1
він навіть не вловлює запити jquery, якщо екземпляр jquery інший. Зміна в прототипі за потреби
— Шишир Арора

3

На додаток до відповіді meouw, мені довелося ввести код у iframe, який перехоплює виклики XHR, і використав вищевказану відповідь. Однак мені довелося змінити

XMLHttpRequest.prototype.send = function(){

До:

XMLHttpRequest.prototype.send = function(body)

І мені довелося змінити

oldSend.apply(this, arguments);

До:

oldSend.call(this, body);

Це було необхідно, щоб він працював в IE9 в режимі документа IE8 . Якщо ця модифікація не була здійснена, деякі зворотні дзвінки, створені компонентною рамкою (Visual WebGUI), не працювали. Більше інформації за цими посиланнями:

Без цих модифікацій передплати AJAX не припинялися.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.