如何从Greasemonkey脚本拦截xmlhttprequest呢?

时间:2022-10-03 21:16:48

I would like to capture the contents of AJAX requests using Greasemonkey.

我希望使用Greasemonkey捕获AJAX请求的内容。

Does anybody know how to do this?

有人知道怎么做吗?

6 个解决方案

#1


39  

The accepted answer is almost correct, but it could use a slight improvement:

公认的答案几乎是正确的,但它可能需要一点改进:

(function(open) {
    XMLHttpRequest.prototype.open = function() {
        this.addEventListener("readystatechange", function() {
            console.log(this.readyState);
        }, false);
        open.apply(this, arguments);
    };
})(XMLHttpRequest.prototype.open);

Prefer using apply + arguments over call because then you don't have to explicitly know all the arguments being given to open which could change!

更喜欢使用apply + parameters而不是call,因为这样您就不必显式地知道要打开的所有参数,哪些参数可能会改变!

#2


4  

How about modifying the XMLHttpRequest.prototype.open or send methods with replacements which set up their own callbacks and call the original methods? The callback can do its thing and then call the callback the original code specified.

修改XMLHttpRequest.prototype怎么样?打开或发送带有替换的方法,这些方法设置自己的回调并调用原始方法?回调可以做它的事情,然后调用回调所指定的原始代码。

In other words:

换句话说:

XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open;

var myOpen = function(method, url, async, user, password) {
    //do whatever mucking around you want here, e.g.
    //changing the onload callback to your own version


    //call original
    this.realOpen (method, url, async, user, password);
}  


//ensure all XMLHttpRequests use our custom open method
XMLHttpRequest.prototype.open = myOpen ;

#3


3  

Tested in Chrome 55 and Firefox 50.1.0

在Chrome 55和Firefox 50.1.0中测试

In my case I wanted to modify the responseText, which in Firefox was a read-only property, so I had to wrap the whole XMLHttpRequest object. I haven't implemented the whole API (particular the responseType), but it was good enough to use for all of the libraries I have.

在我的例子中,我想修改responseText,它在Firefox中是一个只读属性,所以我必须包装整个XMLHttpRequest对象。我还没有实现整个API(特别是responseType),但是对于我拥有的所有库来说,它已经足够好了。

Usage:

用法:

    XHRProxy.addInterceptor(function(method, url, responseText, status) {
        if (url.endsWith('.html') || url.endsWith('.htm')) {
            return "<!-- HTML! -->" + responseText;
        }
    });

Code:

代码:

(function(window) {

    var OriginalXHR = XMLHttpRequest;

    var XHRProxy = function() {
        this.xhr = new OriginalXHR();

        function delegate(prop) {
            Object.defineProperty(this, prop, {
                get: function() {
                    return this.xhr[prop];
                },
                set: function(value) {
                    this.xhr.timeout = value;
                }
            });
        }
        delegate.call(this, 'timeout');
        delegate.call(this, 'responseType');
        delegate.call(this, 'withCredentials');
        delegate.call(this, 'onerror');
        delegate.call(this, 'onabort');
        delegate.call(this, 'onloadstart');
        delegate.call(this, 'onloadend');
        delegate.call(this, 'onprogress');
    };
    XHRProxy.prototype.open = function(method, url, async, username, password) {
        var ctx = this;

        function applyInterceptors(src) {
            ctx.responseText = ctx.xhr.responseText;
            for (var i=0; i < XHRProxy.interceptors.length; i++) {
                var applied = XHRProxy.interceptors[i](method, url, ctx.responseText, ctx.xhr.status);
                if (applied !== undefined) {
                    ctx.responseText = applied;
                }
            }
        }
        function setProps() {
            ctx.readyState = ctx.xhr.readyState;
            ctx.responseText = ctx.xhr.responseText;
            ctx.responseURL = ctx.xhr.responseURL;
            ctx.responseXML = ctx.xhr.responseXML;
            ctx.status = ctx.xhr.status;
            ctx.statusText = ctx.xhr.statusText;
        }

        this.xhr.open(method, url, async, username, password);

        this.xhr.onload = function(evt) {
            if (ctx.onload) {
                setProps();

                if (ctx.xhr.readyState === 4) {
                     applyInterceptors();
                }
                return ctx.onload(evt);
            }
        };
        this.xhr.onreadystatechange = function (evt) {
            if (ctx.onreadystatechange) {
                setProps();

                if (ctx.xhr.readyState === 4) {
                     applyInterceptors();
                }
                return ctx.onreadystatechange(evt);
            }
        };
    };
    XHRProxy.prototype.addEventListener = function(event, fn) {
        return this.xhr.addEventListener(event, fn);
    };
    XHRProxy.prototype.send = function(data) {
        return this.xhr.send(data);
    };
    XHRProxy.prototype.abort = function() {
        return this.xhr.abort();
    };
    XHRProxy.prototype.getAllResponseHeaders = function() {
        return this.xhr.getAllResponseHeaders();
    };
    XHRProxy.prototype.getResponseHeader = function(header) {
        return this.xhr.getResponseHeader(header);
    };
    XHRProxy.prototype.setRequestHeader = function(header, value) {
        return this.xhr.setRequestHeader(header, value);
    };
    XHRProxy.prototype.overrideMimeType = function(mimetype) {
        return this.xhr.overrideMimeType(mimetype);
    };

    XHRProxy.interceptors = [];
    XHRProxy.addInterceptor = function(fn) {
        this.interceptors.push(fn);
    };

    window.XMLHttpRequest = XHRProxy;

})(window);

#4


1  

You can replace the unsafeWindow.XMLHttpRequest object in the document with a wrapper. A little code (not tested):

你可以更换不安全窗口。在文档中使用包装器的XMLHttpRequest对象。一小段代码(未测试):

var oldFunction = unsafeWindow.XMLHttpRequest;
unsafeWindow.XMLHttpRequest = function() {
  alert("Hijacked! XHR was constructed.");
  var xhr = oldFunction();
  return {
    open: function(method, url, async, user, password) {
      alert("Hijacked! xhr.open().");
      return xhr.open(method, url, async, user, password);
    }
    // TODO: include other xhr methods and properties
  };
};

But this has one little problem: Greasemonkey scripts execute after a page loads, so the page can use or store the original XMLHttpRequest object during it's load sequence, so requests made before your script executes, or with the real XMLHttpRequest object wouldn't be tracked by your script. No way that I can see to work around this limitation.

但这有一个小问题:Greasemonkey脚本在页面加载之后执行,因此页面可以在加载序列中使用或存储原始的XMLHttpRequest对象,因此在脚本执行之前发出的请求,或者使用真正的XMLHttpRequest对象时,脚本不会跟踪它们。我不知道如何解决这个限制。

#5


1  

I wrote some code for intercepting ajax calls, when writing proxy server. It should work on most of the browsers.

在编写代理服务器时,我编写了一些拦截ajax调用的代码。它应该在大多数浏览器上运行。

Here it is: https://github.com/creotiv/AJAX-calls-intercepter

这是:https://github.com/creotiv/AJAX-calls-intercepter

#6


0  

Not sure if you can do it with greasemonkey, but if you create an extension then you can use the observer service and the http-on-examine-response observer.

不确定是否可以使用greasemonkey实现,但是如果创建一个扩展,那么可以使用observer服务和http-on- example -response observer。

#1


39  

The accepted answer is almost correct, but it could use a slight improvement:

公认的答案几乎是正确的,但它可能需要一点改进:

(function(open) {
    XMLHttpRequest.prototype.open = function() {
        this.addEventListener("readystatechange", function() {
            console.log(this.readyState);
        }, false);
        open.apply(this, arguments);
    };
})(XMLHttpRequest.prototype.open);

Prefer using apply + arguments over call because then you don't have to explicitly know all the arguments being given to open which could change!

更喜欢使用apply + parameters而不是call,因为这样您就不必显式地知道要打开的所有参数,哪些参数可能会改变!

#2


4  

How about modifying the XMLHttpRequest.prototype.open or send methods with replacements which set up their own callbacks and call the original methods? The callback can do its thing and then call the callback the original code specified.

修改XMLHttpRequest.prototype怎么样?打开或发送带有替换的方法,这些方法设置自己的回调并调用原始方法?回调可以做它的事情,然后调用回调所指定的原始代码。

In other words:

换句话说:

XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open;

var myOpen = function(method, url, async, user, password) {
    //do whatever mucking around you want here, e.g.
    //changing the onload callback to your own version


    //call original
    this.realOpen (method, url, async, user, password);
}  


//ensure all XMLHttpRequests use our custom open method
XMLHttpRequest.prototype.open = myOpen ;

#3


3  

Tested in Chrome 55 and Firefox 50.1.0

在Chrome 55和Firefox 50.1.0中测试

In my case I wanted to modify the responseText, which in Firefox was a read-only property, so I had to wrap the whole XMLHttpRequest object. I haven't implemented the whole API (particular the responseType), but it was good enough to use for all of the libraries I have.

在我的例子中,我想修改responseText,它在Firefox中是一个只读属性,所以我必须包装整个XMLHttpRequest对象。我还没有实现整个API(特别是responseType),但是对于我拥有的所有库来说,它已经足够好了。

Usage:

用法:

    XHRProxy.addInterceptor(function(method, url, responseText, status) {
        if (url.endsWith('.html') || url.endsWith('.htm')) {
            return "<!-- HTML! -->" + responseText;
        }
    });

Code:

代码:

(function(window) {

    var OriginalXHR = XMLHttpRequest;

    var XHRProxy = function() {
        this.xhr = new OriginalXHR();

        function delegate(prop) {
            Object.defineProperty(this, prop, {
                get: function() {
                    return this.xhr[prop];
                },
                set: function(value) {
                    this.xhr.timeout = value;
                }
            });
        }
        delegate.call(this, 'timeout');
        delegate.call(this, 'responseType');
        delegate.call(this, 'withCredentials');
        delegate.call(this, 'onerror');
        delegate.call(this, 'onabort');
        delegate.call(this, 'onloadstart');
        delegate.call(this, 'onloadend');
        delegate.call(this, 'onprogress');
    };
    XHRProxy.prototype.open = function(method, url, async, username, password) {
        var ctx = this;

        function applyInterceptors(src) {
            ctx.responseText = ctx.xhr.responseText;
            for (var i=0; i < XHRProxy.interceptors.length; i++) {
                var applied = XHRProxy.interceptors[i](method, url, ctx.responseText, ctx.xhr.status);
                if (applied !== undefined) {
                    ctx.responseText = applied;
                }
            }
        }
        function setProps() {
            ctx.readyState = ctx.xhr.readyState;
            ctx.responseText = ctx.xhr.responseText;
            ctx.responseURL = ctx.xhr.responseURL;
            ctx.responseXML = ctx.xhr.responseXML;
            ctx.status = ctx.xhr.status;
            ctx.statusText = ctx.xhr.statusText;
        }

        this.xhr.open(method, url, async, username, password);

        this.xhr.onload = function(evt) {
            if (ctx.onload) {
                setProps();

                if (ctx.xhr.readyState === 4) {
                     applyInterceptors();
                }
                return ctx.onload(evt);
            }
        };
        this.xhr.onreadystatechange = function (evt) {
            if (ctx.onreadystatechange) {
                setProps();

                if (ctx.xhr.readyState === 4) {
                     applyInterceptors();
                }
                return ctx.onreadystatechange(evt);
            }
        };
    };
    XHRProxy.prototype.addEventListener = function(event, fn) {
        return this.xhr.addEventListener(event, fn);
    };
    XHRProxy.prototype.send = function(data) {
        return this.xhr.send(data);
    };
    XHRProxy.prototype.abort = function() {
        return this.xhr.abort();
    };
    XHRProxy.prototype.getAllResponseHeaders = function() {
        return this.xhr.getAllResponseHeaders();
    };
    XHRProxy.prototype.getResponseHeader = function(header) {
        return this.xhr.getResponseHeader(header);
    };
    XHRProxy.prototype.setRequestHeader = function(header, value) {
        return this.xhr.setRequestHeader(header, value);
    };
    XHRProxy.prototype.overrideMimeType = function(mimetype) {
        return this.xhr.overrideMimeType(mimetype);
    };

    XHRProxy.interceptors = [];
    XHRProxy.addInterceptor = function(fn) {
        this.interceptors.push(fn);
    };

    window.XMLHttpRequest = XHRProxy;

})(window);

#4


1  

You can replace the unsafeWindow.XMLHttpRequest object in the document with a wrapper. A little code (not tested):

你可以更换不安全窗口。在文档中使用包装器的XMLHttpRequest对象。一小段代码(未测试):

var oldFunction = unsafeWindow.XMLHttpRequest;
unsafeWindow.XMLHttpRequest = function() {
  alert("Hijacked! XHR was constructed.");
  var xhr = oldFunction();
  return {
    open: function(method, url, async, user, password) {
      alert("Hijacked! xhr.open().");
      return xhr.open(method, url, async, user, password);
    }
    // TODO: include other xhr methods and properties
  };
};

But this has one little problem: Greasemonkey scripts execute after a page loads, so the page can use or store the original XMLHttpRequest object during it's load sequence, so requests made before your script executes, or with the real XMLHttpRequest object wouldn't be tracked by your script. No way that I can see to work around this limitation.

但这有一个小问题:Greasemonkey脚本在页面加载之后执行,因此页面可以在加载序列中使用或存储原始的XMLHttpRequest对象,因此在脚本执行之前发出的请求,或者使用真正的XMLHttpRequest对象时,脚本不会跟踪它们。我不知道如何解决这个限制。

#5


1  

I wrote some code for intercepting ajax calls, when writing proxy server. It should work on most of the browsers.

在编写代理服务器时,我编写了一些拦截ajax调用的代码。它应该在大多数浏览器上运行。

Here it is: https://github.com/creotiv/AJAX-calls-intercepter

这是:https://github.com/creotiv/AJAX-calls-intercepter

#6


0  

Not sure if you can do it with greasemonkey, but if you create an extension then you can use the observer service and the http-on-examine-response observer.

不确定是否可以使用greasemonkey实现,但是如果创建一个扩展,那么可以使用observer服务和http-on- example -response observer。