日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

挣脱浏览器的束缚(7) - CrossSubDomainExecutor

發(fā)布時間:2025/7/25 HTML 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 挣脱浏览器的束缚(7) - CrossSubDomainExecutor 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

在上次的文章中,我們已經(jīng)提到了一種能夠跨子域名進行AJAX請求的方法。我們現(xiàn)在就來實現(xiàn)一個對開發(fā)人員透明的實現(xiàn),它會自動判斷這個請求是否是跨子域名,如果不是,則使用傳統(tǒng)的方法發(fā)出AJAX請求,反之則使用我們的方式。

我在如何實現(xiàn)這個Executor的問題上,我想了很久。按照ASP.NET AJAX的“標準”來說,應(yīng)該開發(fā)一個WebRequestExecutor的子類,然后將其設(shè)為默認的Executor或者某個特定WebRequest的Executor。但是最終我使用了直接修改XMLHttpExecutor的做法,因為考慮到以下原因:

  • 完整實現(xiàn)一個WebRequestExecutor需要實現(xiàn)太多接口,而CrossSubDomainExecutor和XMLHttpExecutor有太多相同的地方。
  • 如果繼承WebRequestExecutor的話,還是需要了解XMLHttpExecutor的太多細節(jié),JavaScript做不到太好的封裝,無法實現(xiàn)真正的面向?qū)ο蟆?
  • CrossSubDomainExecutor在普通情況下和XMLHttpExecutor的行為一模一樣。

直接修改XMLHttpExecutor的做法其實就是在XMLHttpExecutor的prototype上做文章,這也就是JavaScript擴展對象的一貫做法。

首先,我們定義一個Sys.Net.WebRequest類的靜態(tài)方法,用來從一個URL中獲得域名。例如輸入http://www.sample.com/Default.aspx這個URL,返回http://www.sample.com,這個靜態(tài)函數(shù)對于判斷是否Cross Domain非常重要,如下:?

Sys.Net.WebRequest._getRawDomain = function(url) {url = Sys.Net.WebRequest._resolveUrl(url);var index = url.indexOf('://');var prefix = url.substring(0, index + 3);var url = url.substring(index + 3);index = url.indexOf('/');if (index < 0){index = url.indexOf('?');}if (index >= 0){url = url.substring(0, index);}return prefix + url; }

?

其次,我們?yōu)镾ys.Net.WebRequest類擴展一個,用于檢測請求能否直接發(fā)出,而不需要使用我們的方法。在這里我們直接使用了一個XMLHttpRequest對象作為嘗試:

Sys.Net.WebRequest.prototype._checkIfIsCrossDomainRequest = function() {var request = new XMLHttpRequest();try{request.open('get', this.get_url());return false;}catch(e){return true;} }

?

我們還需要得到WebRequest的兩個特性:它的Document Domain(設(shè)為document.domain的值),以及它本身的域名(Raw Domain),我們依舊為Sys.Net.Request擴展方法:

Sys.Net.WebRequest.prototype._getDocDomain = function() {if (!this._docDomain){var pageDomain = Sys.Net.WebRequest._getRawDomain(window.location.href);var requestDomain = this._getRawDomain();var i = 0;while (true){i ++;var c1 = pageDomain.charAt(pageDomain.length - i);var c2 = requestDomain.charAt(requestDomain.length - i);if (c1 !== c2){break;}}var url = pageDomain.substring(pageDomain.length - i + 1);var index = url.indexOf('.');this._docDomain = url.substring(index + 1);}return this._docDomain; } Sys.Net.WebRequest.prototype._getRawDomain = function() {if (!this._rawDomain){this._rawDomain = Sys.Net.WebRequest._getRawDomain(this.get_url());}return this._rawDomain; }

?

我們要修改XMLHttpExecutor,最關(guān)鍵的一步就是要重新實現(xiàn)executeRequest方法。很顯然,再這之前,我們需要保留原來的實現(xiàn):

Sys.Net.XMLHttpExecutor.prototype._normalExecuteRequest = Sys.Net.XMLHttpExecutor.prototype.executeRequest;Sys.Net.XMLHttpExecutor.prototype.executeRequest = function() {if (this.get_webRequest()._checkIfIsCrossDomainRequest()){this._crossDomainExecuteRequest();}else{this._normalExecuteRequest();} }

?

接下來,就需要實現(xiàn)一個跨子域名發(fā)出AJAX請求方法了。再這之前,需要對于XMLHttpExecute本身的實現(xiàn)有所了解。我們現(xiàn)在就對添加的方法進行簡單的分析。

首先,自然是_crossDomainExecuteRequest方法。我們準備了兩個字典:_iframeCache和_iframeLoaded,分別用于保存作為Proxy的iframe對象,以及表明iframe對象是否已經(jīng)加載成功了(iframe加載也是異步的,也需要一定時間)。我們先模仿XMLHttpExecutor的實現(xiàn),建立一個偵測是否超時的監(jiān)聽器;再使用該請求的Raw Domain作為key,經(jīng)過下面判斷的三個分支做出不同邏輯。它們是:

  • 如果iframeCache中沒有相應(yīng)的iframe對象,則調(diào)用_createIFrame方法創(chuàng)建一個作為Proxy的iframe。
  • 如果已經(jīng)存在iframe對象,但是Proxy頁面還沒有加載完畢,則等待500毫秒后重新嘗試著調(diào)用_crossDomainExecuteRequest方法。
  • 如果Proxy已經(jīng)加載成功了,并且用戶沒有調(diào)用abort方法,請求也沒有過超時時間,則將document.domain設(shè)為正確值,并且調(diào)用Proxy頁面里的方法發(fā)出AJAX請求。
  • Sys.Net.XMLHttpExecutor._iframeCache = {}; Sys.Net.XMLHttpExecutor._iframeLoaded = {};Sys.Net.XMLHttpExecutor.prototype._crossDomainExecuteRequest = function() {var webRequest = this.get_webRequest();var rawDomain = webRequest._getRawDomain();var iframeCache = Sys.Net.XMLHttpExecutor._iframeCache;var timeout = webRequest.get_timeout();if (timeout > 0){this._timer = window.setTimeout(this._onTimeout, timeout);}if (!iframeCache[rawDomain]){this._createIFrame();}else if (!Sys.Net.XMLHttpExecutor._iframeLoaded[rawDomain]){setTimeout(Function.createDelegate(this, this._crossDomainExecuteRequest),500);}else if (!this.get_aborted() && !this.get_timedOut()){document.domain = webRequest._getDocDomain();iframeCache[rawDomain].contentWindow.sendRequest(this);} }

    ?

    createIFrame方法的作用就是創(chuàng)建一個新的作為Proxy的iframe對象,加載的內(nèi)容即為我們準備的Proxy頁面,請注意Proxy頁面還帶有QueryString,用于指定Proxy頁面的document.domain。我們也會響應(yīng)iframe對象的onload事件。很幸運,這個事件被IE和FireFox瀏覽器都實現(xiàn)了——畢竟FireFox原本就是向IE拿來的iframe,有何道理實現(xiàn)的不同呢?

    Sys.Net.XMLHttpExecutor.prototype._createIFrame = function() {var webRequest = this.get_webRequest();var rawDomain = webRequest._getRawDomain();var proxyUrl = rawDomain + "/SubDomainProxy.htm?" +
    webRequest._getDocDomain();var iframe = document.createElement('iframe');Sys.Net.XMLHttpExecutor._iframeCache[rawDomain] = iframe;iframe.style.display = "none";$addHandler(iframe, 'load',
    Function.createDelegate(this, this._onIFrameLoadHandler)); iframe.src = proxyUrl;document.body.appendChild(iframe);return iframe; }

    ?

    在iframe加載成功時,onIFrameLoadHandler方法會被調(diào)用。在這個方法里,我們將會通過查看Proxy方法有沒有被正確加載來判斷iframe里的Proxy有沒有被加載成功。如果沒有加載成功,則清除iframeCache字典中相應(yīng)的iframe對象,并直接結(jié)束WebRequest請求:清除檢測超時的Timer,調(diào)用WebRequest對象的completed方法等等。如果加載成功了,則在iframeLoaded字典中標記這個iframe已經(jīng)加載成功了,并且在該方法之后重新調(diào)用_crossDomainExecuteRequest方法——只要使用setTimeout再將時間設(shè)為0即可:

    Sys.Net.XMLHttpExecutor.prototype._onIFrameLoadHandler = function() {var webRequest = this.get_webRequest();var rawDomain = webRequest._getRawDomain();var iframeCache = Sys.Net.XMLHttpExecutor._iframeCache;try{document.domain = webRequest._getDocDomain();if (iframeCache[rawDomain].contentWindow.sendRequest){Sys.Net.XMLHttpExecutor._iframeLoaded[rawDomain] = true;setTimeout(Function.createDelegate(this, this._crossDomainExecuteRequest),0);}else{throw new Error();}}catch(e){var iframe = iframeCache[rawDomain];document.body.removeChild(iframe);iframeCache[rawDomain] = null;this._clearTimer();webRequest.completed(Sys.EventArgs.Empty);} }

    ?

    最后我們只要再提供一個作為Proxy的頁面即可,我們必須把它放在每個子域名的根目錄下——當然您也可以改進之前_createIFrame的代碼,加載其他位置上的Proxy頁面。Proxy頁面的實現(xiàn)非常簡單,只要模仿XMLHttpExecutor原有的executeRequest方法實現(xiàn)即可:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head><title>Untitled Page</title><script type="text/javascript" language="javascript">var search = window.location.search;document.domain = search.substring(1);if (!window.XMLHttpRequest){window.XMLHttpRequest = function window$XMLHttpRequest(){var progIDs = [ 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP' ];for (var i = 0; i < progIDs.length; i++){try {var xmlHttp = new ActiveXObject(progIDs[i]);return xmlHttp;}catch (ex) {}}return null;}}function sendRequest(executor){executor._webRequest = executor.get_webRequest();if (executor._started){throw window.parent.Error.invalidOperation(window.parent.String.format(window.parent.Sys.Res.cannotCallOnceStarted,
    'executeRequest'));}if (executor._webRequest === null){throw window.parent.Error.invalidOperation(window.parent.Sys.Res.nullWebRequest);}var body = executor._webRequest.get_body();var headers = executor._webRequest.get_headers();
    executor._xmlHttpRequest = new XMLHttpRequest();executor._xmlHttpRequest.onreadystatechange =
    executor._onReadyStateChange;
    var verb = executor._webRequest.get_httpVerb();executor._xmlHttpRequest.open(verb,
    executor._webRequest.getResolvedUrl(), true);if (headers){for (var header in headers){var val = headers[header];if (typeof(val) !== "function")executor._xmlHttpRequest.setRequestHeader(header, val);}}if (verb.toLowerCase() === "post"){if ((headers === null) || !headers['Content-Type']){executor._xmlHttpRequest.setRequestHeader(
    'Content-Type', 'application/x-www-form-urlencoded');}if (!body){body = "";}}executor._xmlHttpRequest.send(body);executor._started = true;}</script> </head> <body></body> </html>

    ?

    到目前為止,這個CrossDomainExecutor就實現(xiàn)好了,我們現(xiàn)在使用WebRequst時就可以把它的URL設(shè)為不同子域名下的資源。例如,我們在http://www.test.com里可以請求http://sub.test.com或http://sub0.sub1.test.com里的資源。而且,如果不做跨子域名的請求,XMLHttpExecutor的行為和之前不會有任何不同。

    ?

    點擊這里下載源碼。

    轉(zhuǎn)載于:https://www.cnblogs.com/JeffreyZhao/archive/2007/02/05/Break_the_Browsers_Restrictions_7.html

    總結(jié)

    以上是生活随笔為你收集整理的挣脱浏览器的束缚(7) - CrossSubDomainExecutor的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。