Патч обезьяны XMLHTTPRequest.onreadystatechange

Как бы про обезьяну залатать XMLHTTPRequest"s onreadystatechange функция. Я пытаюсь добавить функцию, которая будет вызываться при возвращении каждого ajax-запроса со страницы.

Я знаю, это звучит как ужасная идея, но случай использования довольно своеобразен. Я хочу использовать определенный SDK с консолью (jqconsole), но отображать состояние и результаты вызовов ajax внутри консоли без изменения внешнего SDK.

Я посмотрел на этот пост, в котором была отличная информация, но ничего о обезьяне, исправляющей обратный вызов, которая, кажется, превышает мои навыки JavaScript.

PS Не могу использовать jQuery, поскольку он поддерживает только вызовы ajax, сделанные из jQuery, а не из XMLHTTPRequests прямо здесь.

3 ответа

Решение

К обезьянке-заплатке XMLHttpRequests, вам нужно знать, как обычно строится AJAX-запрос:

  1. Вызов конструктора
  2. Подготовка запроса (setRequestHeader(), open())
  3. Отправка запроса (.send).

Универсальный патч

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

Предыдущий предполагал, что onreadystatechange был назначен на onreadystatechange обработчик. Для простоты я не включил код для других событий, таких как onload, Кроме того, я не учел события, добавленные с помощью addEventListener,

Предыдущий патч работает для всех запросов. Но что, если вы хотите ограничить патч только конкретным запросом? Запрос с определенным URL или асинхронным флагом и конкретным телом запроса?

Условная обезьяна-патч

Пример: перехватывать все POST запросы, тело запроса которых содержит "ТЕСТ"

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

Основные методы, используемые для прозрачного переписывания с использованием...

var original = xhr.method; 
xhr.method = function(){
    /*...*/;
    return original.apply(this, arguments);
};

Мои примеры очень просты и могут быть расширены, чтобы удовлетворить ваши точные пожелания. Это зависит от вас, однако.

Вы можете учиться у Ajax-hook, написанного на китайском!

это расширенный js для включения Monkey patch XMLHTTPRequest

Предполагая, что вы можете игнорировать 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;
}
Другие вопросы по тегам