日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > HTML >内容正文

HTML

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

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

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

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

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

直接修改XMLHttpExecutor的做法其實(shí)就是在XMLHttpExecutor的prototype上做文章,這也就是JavaScript擴(kuò)展對(duì)象的一貫做法。

首先,我們定義一個(gè)Sys.Net.WebRequest類的靜態(tài)方法,用來(lái)從一個(gè)URL中獲得域名。例如輸入http://www.sample.com/Default.aspx這個(gè)URL,返回http://www.sample.com,這個(gè)靜態(tài)函數(shù)對(duì)于判斷是否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類擴(kuò)展一個(gè),用于檢測(cè)請(qǐng)求能否直接發(fā)出,而不需要使用我們的方法。在這里我們直接使用了一個(gè)XMLHttpRequest對(duì)象作為嘗試:

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

?

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

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)鍵的一步就是要重新實(shí)現(xiàn)executeRequest方法。很顯然,再這之前,我們需要保留原來(lái)的實(shí)現(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();} }

?

接下來(lái),就需要實(shí)現(xiàn)一個(gè)跨子域名發(fā)出AJAX請(qǐng)求方法了。再這之前,需要對(duì)于XMLHttpExecute本身的實(shí)現(xiàn)有所了解。我們現(xiàn)在就對(duì)添加的方法進(jìn)行簡(jiǎn)單的分析。

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

  • 如果iframeCache中沒(méi)有相應(yīng)的iframe對(duì)象,則調(diào)用_createIFrame方法創(chuàng)建一個(gè)作為Proxy的iframe。
  • 如果已經(jīng)存在iframe對(duì)象,但是Proxy頁(yè)面還沒(méi)有加載完畢,則等待500毫秒后重新嘗試著調(diào)用_crossDomainExecuteRequest方法。
  • 如果Proxy已經(jīng)加載成功了,并且用戶沒(méi)有調(diào)用abort方法,請(qǐng)求也沒(méi)有過(guò)超時(shí)時(shí)間,則將document.domain設(shè)為正確值,并且調(diào)用Proxy頁(yè)面里的方法發(fā)出AJAX請(qǐng)求。
  • 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)建一個(gè)新的作為Proxy的iframe對(duì)象,加載的內(nèi)容即為我們準(zhǔn)備的Proxy頁(yè)面,請(qǐng)注意Proxy頁(yè)面還帶有QueryString,用于指定Proxy頁(yè)面的document.domain。我們也會(huì)響應(yīng)iframe對(duì)象的onload事件。很幸運(yùn),這個(gè)事件被IE和FireFox瀏覽器都實(shí)現(xiàn)了——畢竟FireFox原本就是向IE拿來(lái)的iframe,有何道理實(shí)現(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加載成功時(shí),onIFrameLoadHandler方法會(huì)被調(diào)用。在這個(gè)方法里,我們將會(huì)通過(guò)查看Proxy方法有沒(méi)有被正確加載來(lái)判斷iframe里的Proxy有沒(méi)有被加載成功。如果沒(méi)有加載成功,則清除iframeCache字典中相應(yīng)的iframe對(duì)象,并直接結(jié)束WebRequest請(qǐng)求:清除檢測(cè)超時(shí)的Timer,調(diào)用WebRequest對(duì)象的completed方法等等。如果加載成功了,則在iframeLoaded字典中標(biāo)記這個(gè)iframe已經(jīng)加載成功了,并且在該方法之后重新調(diào)用_crossDomainExecuteRequest方法——只要使用setTimeout再將時(shí)間設(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);} }

    ?

    最后我們只要再提供一個(gè)作為Proxy的頁(yè)面即可,我們必須把它放在每個(gè)子域名的根目錄下——當(dāng)然您也可以改進(jìn)之前_createIFrame的代碼,加載其他位置上的Proxy頁(yè)面。Proxy頁(yè)面的實(shí)現(xiàn)非常簡(jiǎn)單,只要模仿XMLHttpExecutor原有的executeRequest方法實(shí)現(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>

    ?

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

    ?

    點(diǎn)擊這里下載源碼。

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

    總結(jié)

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

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