如何修改另一个函数接收到的 XMLHttpRequest 响应文本?

新手上路,请多包涵

我正在尝试修改我无法修改的函数接收到的 responseText。此函数创建一个我可以附加到的 XMLHttpRequest,但我无法以允许我在原始函数接收内容之前修改内容的方式“包装”responseText。

这是完整的原始功能:

 function Mj(a, b, c, d, e) {
    function k() {
        4 == (m && 'readyState' in m ? m.readyState : 0) && b && ff(b) (m)
    }
    var m = new XMLHttpRequest;
    'onloadend' in m ? m.addEventListener('loadend', k, !1)  : m.onreadystatechange = k;
    c = ('GET').toUpperCase();
    d = d || '';
    m.open(c, a, !0);
    m.send(d);
    return m
}
function ff(a) {
    return a && window ? function () {
        try {
            return a.apply(this, arguments)
        } catch(b) {
            throw jf(b),
                b;
        }
    } : a
}

我还尝试操纵接收函数 k();为了达到我的目标,但由于它不依赖于传递给函数的任何数据(例如 k(a.responseText);),所以我没有成功。

有什么办法可以做到这一点?我不想使用 js 库(例如 jQuery);


编辑:我知道我不能直接更改 .responseText 因为它是只读的,但我试图找到一种方法来更改响应和接收函数之间的内容。


EDIT2 :在我尝试拦截和更改 .responseText 的方法之一下方添加,该方法已从此处添加: Monkey patch XMLHTTPRequest.onreadystatechange

 (function (open) {
XMLHttpRequest.prototype.open = function (method, url, async, user, pass) {
    if(/results/.test(url)) {
      console.log(this.onreadystatechange);
        this.addEventListener("readystatechange", function () {
            console.log('readystate: ' + this.readyState);
            if(this.responseText !== '') {
                this.responseText = this.responseText.split('&')[0];
            }
        }, false);
    }
    open.call(this, method, url, async, user, pass);
};
})(XMLHttpRequest.prototype.open);


EDIT3 :我忘了包括函数 Mj 和 ff 不是全局可用的,它们都包含在一个匿名函数中 (function(){functions are here})();


EDIT4 :我已经更改了接受的答案,因为 AmmarCSE 没有任何与 jfriend00 的答案相关的问题和复杂性。

简短解释的最佳答案如下:

监听你想要修改的任何请求(确保你的监听器会在原始函数目标之前拦截它,否则在响应已经被使用后修改它是没有意义的)。

将原始响应(如果你想修改它)保存在一个临时变量中

将您要修改的属性更改为“可写:真”,它将删除它具有的任何值。就我而言,我使用

Object.defineProperty(event, 'responseText', {
    writable: true
});

其中 event 是监听xhr请求的 loadreadystatechange 事件返回的对象

现在您可以为响应设置任何您想要的内容,如果您只想修改原始响应,那么您可以使用临时变量中的数据,然后将修改保存在响应中。

原文由 Shadow 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 463
2 个回答

一个非常简单的解决方法是更改 responseText 本身的属性描述符

Object.defineProperty(wrapped, 'responseText', {
     writable: true
});

所以,你可以扩展 XMLHttpRequest 比如

(function(proxied) {
    XMLHttpRequest = function() {
        //cannot use apply directly since we want a 'new' version
        var wrapped = new(Function.prototype.bind.apply(proxied, arguments));

        Object.defineProperty(wrapped, 'responseText', {
            writable: true
        });

        return wrapped;
    };
})(XMLHttpRequest);

演示

原文由 AmmarCSE 发布,翻译遵循 CC BY-SA 3.0 许可协议

编辑:请参阅下面的第二个代码选项(已测试并有效)。第一个有一些限制。


由于您不能修改这些函数中的任何一个,看来您必须遵循 XMLHttpRequest 原型。这是一个想法(未经测试,但你可以看到方向):

 (function() {
    var open = XMLHttpRequest.prototype.open;

    XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
        var oldReady;
        if (async) {
            oldReady = this.onreadystatechange;
            // override onReadyStateChange
            this.onreadystatechange = function() {
                if (this.readyState == 4) {
                    // this.responseText is the ajax result
                    // create a dummay ajax object so we can modify responseText
                    var self = this;
                    var dummy = {};
                    ["statusText", "status", "readyState", "responseType"].forEach(function(item) {
                        dummy[item] = self[item];
                    });
                    dummy.responseText = '{"msg": "Hello"}';
                    return oldReady.call(dummy);
                } else {
                    // call original onreadystatechange handler
                    return oldReady.apply(this, arguments);
                }
            }
        }
        // call original open method
        return open.apply(this, arguments);
    }

})();

这为 XMLHttpRequest open() 方法做了一个猴子补丁,然后当为异步请求调用它时,它为 onReadyStateChange 处理程序做了一个猴子补丁,因为它应该已经设置。然后,在调用原始 onReadyStateChange 处理程序之前,该修补函数会看到 responseText,因此它可以为其分配不同的值。

最后,因为 .responseText 是只读的,这会在调用 onreadystatechange 处理程序之前替换一个虚拟的 XMLHttpResponse 对象。这并非在所有情况下都有效,但如果 onreadystatechange 处理程序使用 this.responseText 来获取响应,则会有效。


并且,这里尝试将 XMLHttpRequest 对象重新定义为我们自己的代理对象。因为它是我们自己的代理对象,所以我们可以将 responseText 属性设置为我们想要的任何值。对于除 onreadystatechange 之外的所有其他属性,此对象只是将 get、set 或函数调用转发给真正的 XMLHttpRequest 对象。

 (function() {
    // create XMLHttpRequest proxy object
    var oldXMLHttpRequest = XMLHttpRequest;

    // define constructor for my proxy object
    XMLHttpRequest = function() {
        var actual = new oldXMLHttpRequest();
        var self = this;

        this.onreadystatechange = null;

        // this is the actual handler on the real XMLHttpRequest object
        actual.onreadystatechange = function() {
            if (this.readyState == 4) {
                // actual.responseText is the ajax result

                // add your own code here to read the real ajax result
                // from actual.responseText and then put whatever result you want
                // the caller to see in self.responseText
                // this next line of code is a dummy line to be replaced
                self.responseText = '{"msg": "Hello"}';
            }
            if (self.onreadystatechange) {
                return self.onreadystatechange();
            }
        };

        // add all proxy getters
        ["status", "statusText", "responseType", "response",
         "readyState", "responseXML", "upload"].forEach(function(item) {
            Object.defineProperty(self, item, {
                get: function() {return actual[item];}
            });
        });

        // add all proxy getters/setters
        ["ontimeout, timeout", "withCredentials", "onload", "onerror", "onprogress"].forEach(function(item) {
            Object.defineProperty(self, item, {
                get: function() {return actual[item];},
                set: function(val) {actual[item] = val;}
            });
        });

        // add all pure proxy pass-through methods
        ["addEventListener", "send", "open", "abort", "getAllResponseHeaders",
         "getResponseHeader", "overrideMimeType", "setRequestHeader"].forEach(function(item) {
            Object.defineProperty(self, item, {
                value: function() {return actual[item].apply(actual, arguments);}
            });
        });
    }
})();

工作演示: http://jsfiddle.net/jfriend00/jws6g691/

我在最新版本的 IE、Firefox 和 Chrome 中尝试过它,它使用一个简单的 ajax 请求。

注意:我没有研究过所有可以使用 Ajax(如二进制数据、上传等)的高级方法,看看这个代理是否足够彻底以完成所有这些工作(我猜它可能还没有没有进一步的工作,但它正在处理基本请求,所以看起来这个概念是有能力的)。


其他失败的尝试:

  1. 尝试从 XMLHttpRequest 对象派生,然后用我自己的构造函数替换构造函数,但这没有用,因为真正的 XMLHttpRequest 函数不允许您将其作为函数调用来初始化我的派生对象。

  2. 尝试覆盖 onreadystatechange 处理程序并更改 .responseText ,但该字段是只读的,因此您无法更改它。

  3. 尝试创建一个虚拟对象,在调用时作为 this 对象发送 onreadystatechange ,但很多代码没有引用 this 而实际有对象保存在闭包的局部变量中 - 从而击败了虚拟对象。

原文由 jfriend00 发布,翻译遵循 CC BY-SA 3.0 许可协议

推荐问题