国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

網(wǎng)頁里做異步的跨域請求

2024-07-21 02:04:35
字體:
供稿:網(wǎng)友
這篇文章將會探討一下在網(wǎng)頁里做異步的跨域請求,以及借助iframe來獲取數(shù)據(jù)的方法。

呃,本來我覺得這個話題沒什么好說的了,因為如今好像沒有幾個web應(yīng)用能離開這類request,google和facebook用iframe來做comet的時候也基本上把能hack的都hack遍了,所以我估計開發(fā)者社區(qū)里應(yīng)該早就形成所謂的”最佳實踐”(best practices)了罷。不過最近看到有一些關(guān)注前端技術(shù)的blog(比如realazy)在討論相關(guān)的話題,發(fā)現(xiàn)還是有一些東西值得寫下來。


一、借助script的異步跨域請求

先說跨域的問題,首先要指出的是,iframe里的js宿主對象一樣也躲不開同源策略(same origin policy),僅僅能解決二級域名的跨域而已,比如www.tudou.com和so.tudou.com,如果要請求某個八桿子打不到一起去的域名下的數(shù)據(jù)(例如你想搞mashup),建議老老實實的用script標(biāo)簽去請求jsonp罷。關(guān)于jsonp要附帶說一下的是,jquery對jsonp請求的封裝方式很值得提倡:

  1. $.getjson(url, params + "&jsoncallback=?", function(json){
  2.     /* do something */
  3. });

用jsoncallback作為服務(wù)器端支持的標(biāo)準(zhǔn)jsonp參數(shù),而每次執(zhí)行這個方法都會用時間戳生成一個唯一的全局函數(shù)名,替換這個“?”,這個細節(jié)被封裝到黑盒里,使用者不必了解,可以像普通的ajax請求一樣,用匿名的回調(diào)函數(shù)作為最末尾的參數(shù)(這是jquery強調(diào)的風(fēng)格),這種語法糖(syntactic sugar)的作用絕對不僅僅是讓前端開發(fā)人員可以偷懶而已,對代碼的可讀性,兼容性和今后的維護都有好處。(我經(jīng)常要向服務(wù)器端的開發(fā)人員解釋這個道理,否則他們才不給你支持什么jsoncallback參數(shù)呢,直接給你返回一個“yy({……})”就算完工……囧)

二、利用前沿技術(shù)的跨域方法

當(dāng)然了,我們還可以走一些目前來看比較野的路子來實現(xiàn)跨域,比如在頁面里嵌入一個不到1k的swf,借助flashplayer向部署了crossdomain.xml的服務(wù)器請求數(shù)據(jù),再用actionscript里的externalinterface類把數(shù)據(jù)還給javascript(我覺得這種方法忒有調(diào)用dll的感覺~大心)。

我們還可以指望ie8里支持的xdomainrequestallowed,和firefox3.1支持的access control,甚至傳說中的html5 socket……噢喔喔,多么甜美的夢……/掐一把臉蛋

三、用iframe直接發(fā)送跨域請求

跟script標(biāo)簽一樣,iframe也可以用來替代ajax,而且在修改document.domain之后(比如上面提到的兩個域名,可以設(shè)置document.domain = “tudou.com”),還可以解決部分跨域問題。

通過iframe請求數(shù)據(jù)的方法,最直接的莫過于在頁面里動態(tài)的嵌入一個iframe標(biāo)簽,用它的src屬性直接請求包含數(shù)據(jù)的網(wǎng)頁,然后利用那個網(wǎng)頁里的js把數(shù)據(jù)傳給父頁面,比如:

  1. <iframe id="crossdomain" width="0" height="0" style="visibility:hidden;" src="http://yoursite.com/request_url/" ></iframe>

這種方法耦合的太緊,非常不推薦。你請求的uri代表一個資源,應(yīng)該是單純的數(shù)據(jù),它會作為xml,json,js代碼還是html來處理,這個并不重要,不應(yīng)該把你的程序邏輯跟數(shù)據(jù)混雜到一起,數(shù)據(jù)也不應(yīng)該因為跨域或不跨域,用iframe,script還是ajax來請求就變成完全不同的東西。

有人會說:為了讓數(shù)據(jù)能夠被js處理,返回的內(nèi)容難免有差異。——但是小的差異可以通過合理的封裝隱藏起來,比如jquery的getjson方法

有人會說:請求的uri是一個動態(tài)頁面,同樣可以在uri里支持類似jsoncallback的參數(shù),生成一個script標(biāo)簽和其中的js代碼,把數(shù)據(jù)“包裹”在js里,比如請求“http://yoursite.com/request_url/?callback=cb1304344”,返回:

  1. <script type="text/javascript">
  2. document.domain="tudou.com";
  3. top.cb1304344({ /* 數(shù)據(jù) */ });
  4. </script>

——首先,很多情況下你請求到的不會是動態(tài)頁面,在這個到處都強調(diào)高負載的web世界里,你拿到的經(jīng)常是squid之類的代理程序返回的緩存。其次,如果你請求的是html格式的文本,為了能作為js代碼來執(zhí)行,服務(wù)器端必須對這段文本做轉(zhuǎn)義和清理工作,而且安全性還不一定能保證(因為html里經(jīng)常包含很多來自ugc的內(nèi)容),如果你請求的是json格式的數(shù)據(jù)……那何必用iframe咧……直接嵌script罷……

四、用iframe直接請求數(shù)據(jù)的最佳實踐

我推薦在上述方法的基礎(chǔ)上做改良,首先在服務(wù)器端,直接返回數(shù)據(jù)本身,并且把數(shù)據(jù)“包裹”在一個textarea標(biāo)簽里,比如:

  1. <textarea><div><p>yyyyy</p></div></textarea>

textarea的優(yōu)點是可以支持任何格式的內(nèi)容,而且這些內(nèi)容不會在iframe子頁面里解析(比如創(chuàng)建dom樹,執(zhí)行js),接下來前端要做的,只是在父頁面里獲取到子頁面的dom,把textarea的內(nèi)容取出來(注意不能取innerhtml而要取value)。

這里存在一個判斷iframe是否加載完成的問題,解決方法之一是在iframe標(biāo)簽上寫onload事件,不過這樣就需要顯式的調(diào)用一個函數(shù)。

方法二如下:

  1. (function(){
  2. try{
  3.     callback(document.getelementbyid('crossdomain').contentwindow.document.body.getelementsbytagname("textarea")[0].value);
  4. }catch(e){
  5.     settimeout(arguments.callee,0);
  6.     return;
  7. }   
  8. })();

最后我們可以封裝出這樣一個方法:

  1. window.tui = window.$ = {};
  2. /**
  3. * @public 通過iframe異步請求數(shù)據(jù)
  4. * @param {string}  url是請求的地址
  5. * @param {function}  cb是處理返回數(shù)據(jù)的回調(diào)函數(shù)
  6. */
  7. tui.getiframedata = function(url, cb){
  8.     var f = document.getelementbyid('crossdomain');
  9.     if(f)
  10.         f.src = url;
  11.     else{
  12.         var t = document.createelement("div");
  13.         t.innerhtml = '<iframe id="crossdomain" width="0" height="0" style="visibility:hidden;" src="' + url + '" ></iframe>';
  14.         document.body.appendchild(t.firstchild);
  15.     }
  16.  
  17.     (function(){
  18.     try{
  19.         cb(document.getelementbyid('crossdomain').contentwindow.document.body.getelementsbytagname("textarea")[0].value);
  20.     }catch(e){
  21.         settimeout(arguments.callee,0);
  22.         return;
  23.     }   
  24.     })();
  25. };
  26.  
  27.  
  28. //像這樣執(zhí)行
  29. $.getiframedata("http://yoursite.com/request_url/", function(data){
  30.     /* do something */
  31. });

只要再增加一個可選的param參數(shù),這就是一個很標(biāo)準(zhǔn)的jquery ajax api,我們還可以在jquery的$.get上面封裝,增加一個是否跨域的判斷,當(dāng)這個request的uri修改成同樣的域名后,自動切換到普通的ajax方法來請求,把返回的文本用類似這樣的正則/<(textarea)>(.+)<//(textarea)>/刪掉多余的字符,再傳給回調(diào)函數(shù),前端和服務(wù)器端都不用修改代碼。

五、用iframe直接請求數(shù)據(jù)的缺陷

必須指出的是,iframe在ie里獲取數(shù)據(jù)時會引發(fā)一些“小問題”,dojo的創(chuàng)始人alex russell把它們稱作“靈異點擊(phantom click)”和“噩夢般的指示器(throbber of doom)”,前者是指在iframe請求內(nèi)容的時候會出現(xiàn)一次點擊鏈接的音效(讓用戶懷疑鬧鬼,多差的體驗口牙!),后者是指iframe加載過程中,ie的界面上會出現(xiàn)正在讀取的提示(比如左下的進度條,右上的圖標(biāo))……好罷,其實以我個人的標(biāo)準(zhǔn),這兩個問題都可以無視……

這種方法還有一個明顯的缺陷,就是只支持get類型的請求。

六、用iframe結(jié)合ajax

不過iframe還有一種使用方法,不但可以避免上面提到的問題,也不需要服務(wù)器端做任何調(diào)整,簡單來說:在iframe的src里調(diào)用一個包含ajax方法的頁面,然后父頁面調(diào)用這個方法來發(fā)起跟子頁面同域名下的ajax請求。在土豆網(wǎng)的播放頁面上,我使用這種方法請求用戶評論統(tǒng)一接口里的html內(nèi)容,例如這個wh40k:dowii的視頻:

http://www.tudou.com/programs/view/ipcprdz_lhi/

獲得評論部分html的接口類似這樣:

http://comments.tudou.com/itemcomment.srv?method=get&iid=21283123&page=1&tm=5&ban=1

這個接口在獨立的一組服務(wù)器上實現(xiàn),在視頻播放頁,豆單播放頁,豆單封面,相冊,個人主頁都會被調(diào)用。由于包含大量用戶提交的內(nèi)容和復(fù)雜的html結(jié)構(gòu),如果用json形式,前端后端處理起來都效率低,此外,提交新評論,回復(fù),刪除,也會用到comments.tudou.com這個域名下的接口,而這些操作顯然需要post類型的請求。在這種需求下,借助iframe的ajax方法

首先在comments.tudou.com域名下部署一個供iframe調(diào)用的跨域文件,感覺很像flashplayer的crossdomain.xml……

http://comments.tudou.com/crossdomain/index.html

可以看到源文件里僅僅包含一個stand-alone的ajax方法……呃……你覺得很眼熟?不用懷疑,就是在jquery源代碼的基礎(chǔ)上修改來的-___-b,支持最基本的需求。這個頁面可以設(shè)置很長的過期頭讓瀏覽器緩存起來,因為不會再有變動。

在父頁面里通過tui.videocomment.request提供統(tǒng)一的接口,不做詳述了,只列舉其中訪問跨域方法的部分:

  1. (function(){
  2.     try{
  3.         $('#crossdomain')[0].contentwindow.tui.ajax(o);   
  4.     }catch(e){
  5.         settimeout(arguments.callee,500);
  6.         return;
  7.     }   
  8. })();

七、總結(jié)一下

iframe適用于 ( 跨域的 && ( 返回大量數(shù)據(jù) || 返回html內(nèi)容 || 需要發(fā)post請求 ) ) 的場合,除此之外還有comet里的串流技術(shù)(streaming)——本文不涉及。使用時需要注意保持資源的純粹性,并盡可能隱藏那些跟其他異步請求差異很大的或包含hack的細節(jié)(比如嵌入iframe,觸發(fā)回調(diào)函數(shù),處理數(shù)據(jù)),設(shè)計出一致的,兼容性和擴展性良好的,不礙眼的接口xd

關(guān)于跨域還要補充一點:修改document.domain可能會產(chǎn)生一些無法預(yù)料的問題,比如在firefox里,document.stylesheets的cssrules屬性會被拒絕訪問。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 云安县| 宁河县| 海门市| 贵阳市| 阿瓦提县| 大名县| 南城县| 河西区| 高邮市| 道孚县| 漳州市| 神农架林区| 潍坊市| 涪陵区| 阳曲县| 德兴市| 巴中市| 安义县| 鞍山市| 南靖县| 襄城县| 巫山县| 泌阳县| 大城县| 修武县| 台中市| 冕宁县| 延津县| 镇安县| 南溪县| 绥江县| 林甸县| 宿州市| 晋城| 长武县| 会同县| 江城| 类乌齐县| 响水县| 芦溪县| 道真|