Страницы

Поиск по вопросам

четверг, 29 ноября 2018 г.

Как перехватить ответ сервера в XMLHttpRequest?

Можно ли имея возможность изменить (inject) XMLHttpRequest изменить его так, чтобы можно было перехватить содержимое ответа не подменяя целиком весь объект со всеми его методами ? Предполагается, что далее в коде будут вызовы вроде:
x = new XMLHttpRequest(); x.open( "GET", "/" ); x.onreadystatechange = function () {...}; x.send( null );
или даже с учетом однопоточности:
x = new XMLHttpRequest(); x.open( "GET", "/" ); x.send( null ); x.onreadystatechange = function () {...};


Ответ

Решение для Blink
Blink - это Chrome, Opera etc.
В прототипе XMLHttpRequest onreadystatechange объявлен как геттер/сеттер и задавая свой геттер/сеттер нужно не сломать логику работы XMLHttpRequest:
var oldXMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function() { var xhr = new oldXMLHttpRequest(); // получаем дескриптор прототипных setter/getter var descrGetSet = Object.getOwnPropertyDescriptor( Object.getPrototypeOf( xhr ), "onreadystatechange" );
var newSet = function( val ) { console.log ( "setter" ); descrGetSet.set.call( xhr, function() { // прототипный setter console.log( "Inject "+this.status ); // this.responseText return val.apply( xhr, arguments ); } ); }
Object.defineProperty( xhr, "onreadystatechange", { set: newSet, // новый setter get: descrGetSet.get // старый getter } );
return xhr; }
Теперь сделав:
x = new XMLHttpRequest(); x.open( "GET", "/" ); x.send( null ) x.onreadystatechange = function () { console.log( this.statusText+' is 2' )};
Увидим
Inject 200 OK is 2 Inject 200 OK is 2 Inject 200 OK is 2
Что и должно быть при смене readyState

Решение для Webkit
Webkit - Safari, PhantomJS
Проблема в этом движке- onreadystatechange это геттер/сеттер объекта XHR, а не XHR.prototype. В итоге переопределить его нельзя. Унаследоваться от XHR тоже нельзя, поэтому нужно применять обертку для XHR Весьма хороша эта обертка: https://github.com/ilinsky/xmlhttprequest

Комментариев нет:

Отправить комментарий