離線應用系統的設計目標就是在網絡離線情況下依然可以操作我們的應用系統,並在網絡暢通的情況下與服務器進行數據交互。
所以離線應用系統最終會做成類似C/S架構的客戶端應用程序。這邊基於Chrome或者 Safari瀏覽器的 Web Application(Web 應用程序插件)無疑是最好的選擇。
這邊以Chrome 的 Web Application 為例,離線系統做成Web 應用程序之后,與服務端的交互就變成最麻煩的一件事了,因為離線Web應用程序是安裝在各個用戶的瀏覽器上面,而最終的Server,只是一個由終端服務方提供的固定地址。那么從本地瀏覽器對Server發起請求,就存在這跨域通信的情況。
JavaScript出於安全方面的考慮,不允許跨域調用其他頁面的對象。因為同源策略的限制,域名www.xx1.com下的js無法訪問和操作www.xx2.com域名下的對象。
下面是一個關於跨域訪問的說明清單,摘自博客園的一篇文章:
URL |
說明 |
是否允許通信 |
http://www.a.com/a.js |
同一域名下 |
允許 |
http://www.a.com/lab/a.js |
同一域名下不同文件夾 |
允許 |
http://www.a.com:8000/a.js |
同一域名,不同端口 |
不允許 |
http://www.a.com/a.js |
同一域名,不同協議 |
不允許 |
http://www.a.com/a.js |
域名和域名對應ip |
不允許 |
http://www.a.com/a.js |
主域相同,子域不同 |
不允許 |
http://www.a.com/a.js |
同一域名,不同二級域名(同上) |
不允許 (cookie這種情況下也不允許訪問) |
http://www.cnblogs.com/a.js |
不同域名 |
不允許 |
HTML5提供了跨文檔消息機制(Cross Document Messaging),它具備了跨越frame、tabs或windows通信的能力。
PostMessage API
window.postMessage是安全啟用跨域通信的方法。通常情況下,不同的頁面上的腳本只允許相互訪問和執行他們的網頁,window.postMessage提供了一個控制機制,以規避此限制的方式是安全的。
瀏覽器支持情況:
瀏覽器類型 |
版本號 |
Chrome |
2.0版本及以上 |
FireFox |
3.0版本及以上 |
Internet Explorer |
8.0版本及以上 |
Opera |
9.6版本及以上 |
Safari |
4.0版本及以上 |
數據發送方:otherWindow.postMessage(message, targetOrigin);
otherWindow
對另一個窗口的引用,這邊可以使用的contentWindow屬性的iframe元素,或通過window.open返回的對象,或通過frames命名window.frames。
message
發送到其它窗口的數據。
targetOrigin
數據發送的目標地址。
數據接收方:
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{ if (event.origin !== "http://XXXX") return;
// ... }
event.data
獲取從其他窗口傳輸過來的數據對象。
event.origin
postMessage的發送消息時的窗口的起源。這個字符串是該協議的串聯和“:/ /”,主機名(如果有的話),和“:”如果一個端口,並從給定的協議的默認端口不同的端口號。
event.source
一個窗口之間建立雙向溝通兩個不同來源的窗戶,代表了你目前的接收方的窗口,你可以使用這個對象發送消息。
我們離線系統中的Information.htm頁面保存的是當前登錄用戶的相關信息,假設現在的網絡是在線情況下,那我們的保存功能就需要同步保存到服務端。所以我們的Information.htm頁面除了保存之外,還多了跨域通訊相關實現代碼。
同樣的,我們也需要一個跨域訪問的服務端,那我們就做個服務端,設計數據庫如下:
這個表的設計是相對於我們客戶端的數據表,用來保存或者修改客戶端提交上來的數據。然后我們在服務端寫好與之相關的操作就行了。服務端這邊也要建立一個頁面:crossDomain.htm,作為跨域訪問的源(附件源碼中有關於服務端這塊的代碼)。在crossDomain.htm頁面里面我們寫好程序處理客戶端傳遞過來的值。
代碼如下:

1 //這邊添加了一個監聽事件,監聽客戶端傳遞過來的值,並進行處理 2 window.addEventListener("message", receiveMessage, false); 3 function receiveMessage(event) { 4 SaveUserInfo(event.data, event.source, event.origin); 5 } 6 7 //這邊是處理程序 8 //我們在這邊做了個Web服務,把Json化的頁面表單拿進去保存 9 //還有一步就是將執行的結果進行反饋,event.source指的是發送方的ContentWindow, 10 //event.origin指的是發送方的URL地址。這樣就實現了我們向發送源反饋信息結果的目的。 11 //這種方式叫做跨域通信的對話機制 12 function SaveUserInfo(UserInfoStr,source,origin) { 13 $.ajax({ 14 type: "POST", 15 contentType: "application/json;utf-8", 16 url: "WebService.asmx/PostInformation", 17 data: "{UserInfoStr:'" + UserInfoStr + "'}", 18 dataType: 'json', 19 anysc: false, 20 success: function (data) { 21 if (data.d=="1") { 22 source.postMessage("保存成功!",origin); 23 } 24 else { 25 source.postMessage("保存失敗!", origin); 26 } 27 } 28 }); 29 }
現在我們來看一下發起跨域請求的頁面是怎么寫的,寫在我們的Information.htm頁面里。
首先,我們加一個iframe元素,用來鏈接我們跨域交互的那個頁面,然后我們獲取那個iframe的contentWindow,並以它為源來發送信息。
"http://192.168.21.22:86/CRXServer/crossDomain.htm":假設這個地址則是我們服務端頁面的所在地址,這樣子,我們就把表單的數據發送過去了。
代碼如下:

1 function crossDomain() { 2 var UserInfoJson = UserInfo_Serialy(); 3 var ifrCrossDomain = document.getElementsByTagName("iframe")[0].contentWindow; 4 ifrCrossDomain.postMessage(UserInfoJson, 5 "http://192.168.21.22:86/CRXServer/crossDomain.htm"); 6 } 7
服務端接收到表單數據之后,進行了數據處理,處理完成之后反饋信息到發送端(也就是我們發起請求的客戶端),我們的發送端接收到信息,並進行處理,所以發送端這邊還需要加一個監聽事件:

1 window.addEventListener("message", receiveMessage, false); 2 function receiveMessage(event) { 3 alert(event.data); 4 }
一旦監聽到服務端向我們返回數據,我們就輸出數據。
這樣,流程環節就出來了:
提交保存之后,我們會彈出一個對話框,顯示“保存成功!”,那顯示的是服務端返回的數據,同時,數據庫中也保存進了數據。
關於XMLHttpRequest Level 2
HTML5中定義了XMLHttpRequest Level 2,它有兩方面的增強:跨域通信,通信進度通知(progress events),有興趣的可以了解下:
http://www.html5rocks.com/en/tutorials/file/xhr2/
關於Chrome Web Application(chrome Web 應用程序插件)中的跨域:
{
"name": "My extension",
...
"permissions": [
"http://192.168.21.22/"
],
...
}
這樣就可以直接在插件里面的腳本中訪問服務端的數據,當然安全性方面需要設置,這些我們會在后面的Chrome Web Application章節里詳細了解。
源碼參考:CrossCommunication