上次給我的閉關紀要開了一個頭之後,我就一直繼續閉關研究,雖然我研究的東西大多很有來頭,而且我也預計將源碼公開,可是都還沒有一個完整的東西來給大家介紹,幾天前,我在解決我的網站效能問題的時候突然有一個想法,能夠解決我一直很想解決的子網域名稱跨域的問題,覺得應該與大家分享一下,不知道有沒有人用過。
問題描述如下:我個人使用Ajax的方式通常是調用類似Json或者Rest介面,這些介面都是通過Script標籤傳遞資料,不過這樣使用的問題在於只能通過非同步來載入資料,很多時候會造成程式架構上的不便,遠不如直接使用Xml或者文字檔的同步訪問模式(XML Http Request)來的直接,可是如果用XML Http Request的模式,就會產生跨域問題,這樣,必須要把頁面和XML服務部署在同一個網域名稱上,哪怕是同一個網域名稱的子網域名稱也不行。
假如我希望通過Ajax的模式,由http://www.dituren.cn/ 頁面上的代碼去訪問http://51ditu.dituren.cn/ 上面的XML或者文本服務,有辦法實現嗎?
或許你認為通過頁面上設定document.domain可以解決,不過這是白費心機,我已經研究過了,這樣是行不通的!在HTML頁面上為什麼可以呢?因為兩個頁面都可以這樣設定網域名稱:document.domain='dituren.cn';這樣他們就是在同一個域了,可是頁面去訪問XML的時候,XML顯然沒有設定自己的domain的地方,因此就會產生跨域訪問,使用者得到一個“沒有許可權”的提示。
既然是這樣,那麼我想,為什麼不在http://51ditu.dituren.cn/ 上面使用一個代理頁面,設定自身的document.domain='dituren.cn',而www域下的網頁通過Iframe載入這個頁面,並且也設定自己的域,這樣的話,這兩個頁面之間的交流是暢通的,而這個代理頁面本身是在51ditu域下的,因此肯定可以訪問本域的XML服務了。
以上想法經過我的測試(IE6,IE7,FF3),完全可用!因此假如您已經理解我的意思,或者暫時不需要此技巧,可以不再向下看了,假如您想參考我的具體使用方法,可以向下看,說不定能對您有所啟發。
先看看代理頁面的代碼吧:
1<html>
2<head>
3<script language="javascript">
4 //本函數在頁面載入的時候運行
5 function onLoad()
6 {
7 document.domain="dituren.cn"//設定自身的域
8 var hash=location.hash;//父視窗會使用錨點參數傳遞一個回呼函數名稱過來
9 if(hash&&hash.length>1)//如果回呼函數名稱存在
10 {
11 hash=hash.substring(1);
12 parent[hash](self);//調用回呼函數,通知父視窗代理已經準備好了
13 }
14 }
15 //本函數會由父視窗調用,返回一個XMLHttpRequest對象給父視窗,剩下的操作父視窗自己完成
16 function createHttpRequest()
17 {
18 if(window.XMLHttpRequest)
19 {
20 return new XMLHttpRequest();
21 }
22 else if(typeof(ActiveXObject)!="undefined")
23 {
24 return new ActiveXObject("Microsoft.XMLHTTP");
25 }
26 }
27</script>
28</head>
29<body onload="onLoad()"></body>
30</html>
代碼不算複雜吧,這是一個靜態檔案現在被我部署到了http://51ditu.dituren.cn/51ditu/ajaxAgent.htm ,我的其它子域頁面上假如需要調用http://51ditu.dituren.cn/ 上面的服務,可以採用類似於以下代碼:
1 window.onAgentLoaded=function(win){window.agentWindow=win;}//設定回呼函數,在被agent頁面回調的時候設定window.agentWindow變數
2 document.domain="dituren.cn";//設定自己的域
3 document.write('<iframe src="http://51ditu.dituren.cn/51ditu/ajaxAgent.htm#K_CPoint_AgentLoaded" style="display:none;"></iframe>');//在頁面上通過Ajax載入Agent並通過錨點將回呼函數名稱提供給Agent頁面
4
5 //頁面上的其它程式通過調用此函數來獲得訪問ajax的request對象
6 function createHttpRequest()
7 {
8 if(window.agentWindow){return window.agentWindow.createHttpRequest();}
9 else
10 {
11 //假如調用服務的時候AjaxAgent還沒有載入完成,這個時候怎麼辦?
12 //我的邏輯是調用本域的,因為我的服務在每個域都有部署,只是為了效能才需要調用其它域的(另外的伺服器)
13 }
14 }
上面的代碼已經擷取了一個HttpRequest對象,怎麼使用就不用我在這裡說明了,總之用這個方法實現了跨子網域名稱的Ajax訪問,這是我最高興的。
一直以來,我都希望能夠將我的頁面部署在一個很穩定的伺服器,而將一些附加的服務部署在另一個地方,因為這些附加的服務有些是很耗效能的,避免對首頁面伺服器產生比較大的影響,這次通過這個方法,我終於可以做到這個了。
這次網站從http://www.step1.cn/ 遷移到http://www.dituren.cn/ 一個很重要的事情就是網域名稱的分拆,以前將很多的內容都部署在一個www網域名稱下,現在,被我拆成了很多子網域名稱,在後面,我會逐次的提到每個網域名稱的作用和相關的核心技術。