WeblogicPortal中實現AJAX編程之架構
摘要
門戶應用程序非常適用于從多個源提取信息以及為包含門戶Web應用程序的portlet提供應用服務。對于用戶,portlet應用程序是獨立的實體,類似于桌面上的窗口應用程序。如果在一個窗口應用程序中執行一項操作會導致其他所有應用程序中的內容被刷新,那又會怎么樣呢?這就是當前大多數門戶的情況。在一個portlet中通過頁面流進行轉移會導致整個Web頁面被刷新,包括該頁面上的其他所有portlet。
為了避免出現這種有時不希望有的行為,Web開發人員采用了所謂Ajax-風格的編程方法。Ajax即異步Java和XML(Asynchronous Java and XML),它是一個技術的集合,包括用于創建交互式Web應用程序的XHTML、CSS、JavaScript、DOM和XmlHttpRequest對象。本文將說明在BEA WebLogic Portal環境中使用Ajax編程方法的基本原理,并提供了一些最佳實踐和建議,以避免新手Ajax程序員經常會犯的許多錯誤。
Ajax簡介
考慮一個基于一些用戶標準(比如街道地址、城市和州,)來繪制街道地圖的Web應用程序。這類應用程序在用戶界面中已經存在很多年了,并且很少有所改變。用戶輸入一個地址,然后單擊一個按鈕,頁面中心就會顯示周邊區域的地圖。用戶通常需要縮小和放大以更清楚地顯示周圍區域,或者需要把地圖向左或向右稍作移動,以找到一些能夠幫助他們進行定位的主要街道或地界標。訪問maps.yahoo.com或mapquest.com,便可以獲得這種體驗。來吧,試試這個地址:100 East Wacker Drive Chicago, IL
最終,您將看到一幅芝加哥市區的地圖,中心是Wacker和Michigan大街。在右方,您將看到一系列縮放級別,您可以從中選擇一個。當選定一個縮放級別之后,會出現什么情況呢?整個頁面將會刷新,這會花費相當長一段時間。現在,當您把地圖向左或向右移動時,又會出現什么情況呢?您不可避免地會刷新整個頁面,而整個地圖需要再次下載。頁面大小通常是75到100k,而平均的返回時間大約是3到10秒鐘,這取決于您的網絡連接速度。
現在使用Google Maps進行同樣的嘗試,這是一個完全使用Ajax技術的站點。輸入地址,然后單擊Search。頁面將完全顯示出來。現在進行縮放。注意,地圖之外的頁面內容不會刷新。下面列出了這背后所發生的事情:
有關Ajax的完整介紹,請閱讀An Introduction to Ajax(中文版,dev2dev,2005年11月)。
Ajax所解決的門戶問題
考慮那些大量使用Java applet而且希望利用其現有資產創建門戶的潛在客戶。把現有applet和其他頁面流包裝到portlet容器中是一件很簡單的事情,但是也要考慮到進行門戶測試時會出現哪些問題。例如,某個porlet中的一個動作會導致刷新以及隨后的重新加載,并重新初始化頁面上其他所有基于applet的portlet。如果所討論的applet具有后端連接,那么portlet的重新初始化將導致服務器丟棄現有連接,然后強制applet重新進行連接,這不僅加重了服務器的負擔,而且會使門戶用戶看到幾秒鐘的“靜止時間”,在這段時間內,基于applet的portlet必須保持灰色,一直到它們完成初始化為止。
這顯然是一個潛在的瑕疵。告訴客戶們重寫所有的applet,因為基于JSP的應用程序并非有用的響應,尤其是對于已經在現有資產中投入大量資源的客戶來說。此外,門戶必須幫助客戶包裝現有的應用程序,而不是強迫他們重寫整個系統。
我們需要一種讓單個portlet在不引起頁面刷新的情況下進行操作或獲得新數據的方法。雖然這顯然存在一些不利之處(我將在本文后面討論這些),但是在這種情況下,把避免頁面刷新作為使用門戶的進入屏障是完全有必要的。
注:對于這個問題,一個可行的解決方案是iframes,也就是inline frames,一種基于瀏覽器的機制,它可以使屏幕的一塊區域變為獨立的實體,當頁面重新加載時它不會刷新。使用類似于Ajax編程中所使用的技術,我們可以使用XML-RPC進行服務器調用,從而獲取數據并將其加載到DOM文檔中。Apple的開發者Web站點上有一篇文章非常好地總結了這種方法的優點和缺點。我確信,關于為什么iframes更好或Ajax更好,雙方的支持者已經進行過精彩的辯論,但是Ajax已經流行開來,而iframes則沒有。因此,本文只介紹了Ajax,而沒有就iframes進行討論。
用例
在下列情形下,Ajax技術很有用處:
Ajax技術的核心是Web瀏覽器對一些負責提供信息的Web服務的調用。如何為該解決方案設計架構呢?有3種值得考慮的架構。第一種架構使用Web瀏覽器作為集成點,出于隨之而來的安全考慮和瀏覽器方面的問題,該架構存在問題。第二種解決方案使用代理來獲取分布的資源,這消除了安全問題,但是添加了一個單點故障。第三種架構使用企業服務總線(enterprise service bus,ESB)來消除單點故障,并為Web服務和Ajax技術的蓬勃發展提供了最適宜的環境。
以瀏覽器為中心
在以瀏覽器為中心的架構中,Web瀏覽器成為聯系遠程系統的中心點,如圖1所示。數據從各個遠程系統中獲得,然后在瀏覽器處的JavaScript中進行整理和排序。

圖 1.以瀏覽器為中心的架構
這種架構是最自然的設計,它具有多處設計缺陷:
如圖2所示,代理服務消除了第一種架構的所有缺陷,但是它也有自己的不足之處。它導致出現了一個系統單點故障。當然,Web服務器可以作為帶有硬件負載平衡器的集群的一部分。這無疑可以解決這個問題。

圖 2. 代理服務架構
盡管這種架構比起以瀏覽器為中心的架構已經有了巨大的改進,還可以做得更好。我們已經為Web開發人員提供了一個非常好的環境,但是這很可能為后端系統開發人員帶來不便。對遠程系統進行身份驗證以及數據整理仍然是必要的,但是至少后端程序員應該有更多可使用的工具來迎接挑戰。
企業服務總線
圖3與前面的兩幅圖又有所不同,因為企業服務總線(ESB)架構是一種邏輯架構。服務可以位于網絡上的任意位置,數據源可以被抽象到(可插入到ESB中的)服務中。ESB為后端系統開發人員處理了他們通常必須做的工作,比如服務身份驗證、數據轉換、協議轉換和可靠性特征。ESB可以擴展到帶有硬件負載平衡器的計算機集群上,這提供了代理服務架構所具有的優點。

圖 3.企業服務總線
為了說明在門戶中對Ajax應用程序使用ESB的優點,考慮對于每次Ajax調用都要引用一個Web服務的情況。對于小型門戶來說,Web服務的數量可能相對較少,譬如說幾十個。但是隨著門戶的增長,將會引入更多的Web服務。大量Web服務的添加與Ajax沒有關系,但是與SOA的實現有著密切的聯系。這是一個ESB的經典用例。盡管從嚴格意義上來說,它對于Ajax實現的操作不是必要的,但是它還是帶來了幾個好處:
對門戶使用的影響
當在門戶內采用Ajax編程方法時,出現了幾個問題。這些問題中的大多數與門戶的生命周期以及如何/何時挑選用戶信息有關。具體來說,門戶要使用諸如用戶在哪里單擊之類的信息來決定向其顯示何種類型的相關信息。WebLogic Portal具有一種叫做campaign的特性,它允許門戶設計人員基于用戶個人信息指定對用戶的有目的廣告宣傳。用戶個人信息中包括用戶的頁面歷史,即,用戶過去點擊過的頁面。門戶在頁面刷新時收集這類信息,所以如果用戶從未刷新頁面,門戶就無法(容易地)自動收集用戶信息。
考慮Ajax編程可能對門戶產生的以下副作用:
一種最佳實踐是在所有腳本中使用獨有的變量和函數名稱,具體做法是在每個變量和函數的名稱前加上包含它們的portlet的名稱。
瀏覽器戰爭尚未結束,專有瀏覽器和基于標準的瀏覽器之間的戰爭仍在延續。Ajax相當有趣的一點是,其中有一半是標準(XHTML、XSLT、JavaScript/ECMAScript、DOM和Web services)驅動的。但是其核心技術——XmlHttpRequest對象——來自微軟。
下面列出了在進行跨瀏覽器的Ajax編程時要注意的一些重要的常見錯誤,以及如何避免這些陷阱。
安全性
XmlHttpRequest對象直接把瀏覽器連接到一臺遠程主機,要么是加載頁面的Web服務器,要么是從另一個完全不同的服務器。正如您所想像的,這里為惡意軟件提供了大量的機會。例如,一段惡意的JavaScript可能等著用戶輸入口令字段,然后把口令傳遞給一個遠程瀏覽器,而用戶卻不知道,甚至還沒有單擊頁面上的提交按鈕。如果把口令換為信用卡號碼,事情就變得更加有趣了。
為了避免這種風險,Mozilla拒絕到為Web頁面提供服務的主機之外的任意主機的連接。用戶不會看到錯誤消息,因為它根本就不會運行!
Internet Explorer (IE)采用另一種方法。當要求連接到遠程主機時,將使用一個對話框通知用戶,而用戶可以決定執行什么操作。但是要注意,該對話框不會告訴用戶要連接到哪個站點,所以用戶缺乏可以用于做出決策的信息。

圖 4. 沒有有用的信息!
這個問題的解決方案是使用一個Java servlet作為到Web服務的代理。該servlet獲得所有的參數,并把它們傳遞給遠程服務,接著將響應返回給Web站點。通過讓servlet運行在創建Web頁面的Web服務器上,Mozilla就會認為服務是本地的。注意,這是企業服務總線(比如AquaLogic Service Bus)的一個絕好用例。
使用XmlHttpRequest對象
包含XmlHttpRequest對象的XmlHttp庫最初是隨Internet Explorer 4一起發行的ActiveX控件。Mozilla包含一個兼容的函數庫,所以沒有什么好擔心的。我仍然推薦使用一個開源庫,比如Sarissa或DWR。然而,它們在將XML數據傳遞到對象的方式上存在著細微的區別。
更新DOM節點
Mozilla和IE之間最令人惱火的區別就是Web頁面中對DOM(Document Object Model,文檔對象模型)的處理。大多數函數的工作方式是一樣的(至少在DOM Level 2上),但是仍然有很多值得注意的地方。下面給出兩個例子。
innerHtml與innerText的使用
當使用新的動態內容更新<div>標簽時,IE用戶有兩個選擇:可以更新節點的innerText或innerHTML。二者的區別很細微,但是卻能在信息的顯示方面造成很大的差別,尤其是當要顯示的文本是XML格式或者包含HTML實體(比如尖括號或&符號)時。使用innerHTML時假定內容與標簽是一起放入的(不管內容是什么)。假設我們試著把以下文本放入一個節點中:
<div id="mydivhtml">部分在瀏覽器中看起來如下:
<div id="mydivtext">部分看起來則是下面這樣:
當某個門戶頁面由WebLogic Portal(或任何門戶)進行解析時,每個portlet均作為完整的Web頁面放在HTML文檔中,包括<body>標簽(有時甚至包括<html>標簽)。因此,如果在每個portlet中始終以相同的ID來命名<div>標簽(理論上來說,這似乎是使編程標準化的一種良好方法),那么將獲得不正確的結果。考慮如果有兩個ID為“result_data”的元素,那么解析后的門戶頁面將會是什么樣子。
因此,這里的最佳實踐是使用portlet名稱作為HTML標簽中所有ID的前綴。
結束語
傳統的Web應用程序已經無法滿足客戶的要求。由于Google之類的公司提供了更新、更快和交互性更強的Web站點,客戶的期望值變得越來越高。構建使客戶可以提高工作速度和效率的用戶界面始終是一項戰略性挑戰。
Ajax編程是Web應用程序交互方面的新的事實標準,它為緊密耦合的數據和應用程序筒倉提供了部分解決方案。特別是在與BEA WebLogic平臺結合使用時,Ajax代表了Web編程的未來方向,并預示著構建具有高度的交互性和響應靈敏度的Web站點的新潮流。
在本系列的第二部分中,我將使用具體的例子說明如何在WebLogic Portal環境中使用Ajax。具體來說,我將演示大量portlet,以說明如何從JavaScript調用Web服務以及使用結果更新頁面、如何實現代理servlet來處理對外部Web服務的調用、如何使用Ajax把Web頁面嵌入到另一個Web頁面中,以及如何更新您自己的可更新數據庫表小構件。
門戶應用程序非常適用于從多個源提取信息以及為包含門戶Web應用程序的portlet提供應用服務。對于用戶,portlet應用程序是獨立的實體,類似于桌面上的窗口應用程序。如果在一個窗口應用程序中執行一項操作會導致其他所有應用程序中的內容被刷新,那又會怎么樣呢?這就是當前大多數門戶的情況。在一個portlet中通過頁面流進行轉移會導致整個Web頁面被刷新,包括該頁面上的其他所有portlet。
為了避免出現這種有時不希望有的行為,Web開發人員采用了所謂Ajax-風格的編程方法。Ajax即異步Java和XML(Asynchronous Java and XML),它是一個技術的集合,包括用于創建交互式Web應用程序的XHTML、CSS、JavaScript、DOM和XmlHttpRequest對象。本文將說明在BEA WebLogic Portal環境中使用Ajax編程方法的基本原理,并提供了一些最佳實踐和建議,以避免新手Ajax程序員經常會犯的許多錯誤。
Ajax簡介
考慮一個基于一些用戶標準(比如街道地址、城市和州,)來繪制街道地圖的Web應用程序。這類應用程序在用戶界面中已經存在很多年了,并且很少有所改變。用戶輸入一個地址,然后單擊一個按鈕,頁面中心就會顯示周邊區域的地圖。用戶通常需要縮小和放大以更清楚地顯示周圍區域,或者需要把地圖向左或向右稍作移動,以找到一些能夠幫助他們進行定位的主要街道或地界標。訪問maps.yahoo.com或mapquest.com,便可以獲得這種體驗。來吧,試試這個地址:100 East Wacker Drive Chicago, IL
最終,您將看到一幅芝加哥市區的地圖,中心是Wacker和Michigan大街。在右方,您將看到一系列縮放級別,您可以從中選擇一個。當選定一個縮放級別之后,會出現什么情況呢?整個頁面將會刷新,這會花費相當長一段時間。現在,當您把地圖向左或向右移動時,又會出現什么情況呢?您不可避免地會刷新整個頁面,而整個地圖需要再次下載。頁面大小通常是75到100k,而平均的返回時間大約是3到10秒鐘,這取決于您的網絡連接速度。
現在使用Google Maps進行同樣的嘗試,這是一個完全使用Ajax技術的站點。輸入地址,然后單擊Search。頁面將完全顯示出來。現在進行縮放。注意,地圖之外的頁面內容不會刷新。下面列出了這背后所發生的事情:
- 用戶單擊縮放控件。
- 縮放控件調用一個JavaScript方法。
- JavaScript方法調用服務器來請求新的地圖信息。
- 服務器創建新的圖像,然后將其發回給瀏覽器。
- 瀏覽器使用新的圖像數據替換現有的圖像數據。
- 用戶在舊圖像的位置看到新圖像。
- 用戶把地圖向左方拖動。
- Web頁面使用它所下載的圖像信息,而用戶等待著頁面根據客戶端已經存在的數據重新繪制地圖。
- 如果預先沒有檢索到圖像數據,瀏覽器將從服務器獲得它所需要的附加信息(新出現的地圖區域)。
- 新的數據被添加至現有數據,并在瀏覽器中重新繪制出來。
- 用戶看見地圖向右方滾動,就像是一幅真正的大圖那樣。
- 客戶端與服務器之間傳輸的數據量大大減少。
- 可感知的系統響應時間大幅度縮短。
- 由于反饋及時,用戶感覺對應用程序的控制更加得心應手。
- 用戶喜歡這個站點,因為使用它可以提高他們的工作效率。
- 用戶將會再次訪問站點,這樣其競爭對手的客戶就會減少。
有關Ajax的完整介紹,請閱讀An Introduction to Ajax(中文版,dev2dev,2005年11月)。
Ajax所解決的門戶問題
考慮那些大量使用Java applet而且希望利用其現有資產創建門戶的潛在客戶。把現有applet和其他頁面流包裝到portlet容器中是一件很簡單的事情,但是也要考慮到進行門戶測試時會出現哪些問題。例如,某個porlet中的一個動作會導致刷新以及隨后的重新加載,并重新初始化頁面上其他所有基于applet的portlet。如果所討論的applet具有后端連接,那么portlet的重新初始化將導致服務器丟棄現有連接,然后強制applet重新進行連接,這不僅加重了服務器的負擔,而且會使門戶用戶看到幾秒鐘的“靜止時間”,在這段時間內,基于applet的portlet必須保持灰色,一直到它們完成初始化為止。
這顯然是一個潛在的瑕疵。告訴客戶們重寫所有的applet,因為基于JSP的應用程序并非有用的響應,尤其是對于已經在現有資產中投入大量資源的客戶來說。此外,門戶必須幫助客戶包裝現有的應用程序,而不是強迫他們重寫整個系統。
我們需要一種讓單個portlet在不引起頁面刷新的情況下進行操作或獲得新數據的方法。雖然這顯然存在一些不利之處(我將在本文后面討論這些),但是在這種情況下,把避免頁面刷新作為使用門戶的進入屏障是完全有必要的。
注:對于這個問題,一個可行的解決方案是iframes,也就是inline frames,一種基于瀏覽器的機制,它可以使屏幕的一塊區域變為獨立的實體,當頁面重新加載時它不會刷新。使用類似于Ajax編程中所使用的技術,我們可以使用XML-RPC進行服務器調用,從而獲取數據并將其加載到DOM文檔中。Apple的開發者Web站點上有一篇文章非常好地總結了這種方法的優點和缺點。我確信,關于為什么iframes更好或Ajax更好,雙方的支持者已經進行過精彩的辯論,但是Ajax已經流行開來,而iframes則沒有。因此,本文只介紹了Ajax,而沒有就iframes進行討論。
用例
在下列情形下,Ajax技術很有用處:
- 門戶中使用了一個或多個基于applet的portlet。
例子:參見前面內容中描述的場景。 - portlet需要定期刷新其數據或重繪其內容。
例子:一個帶有股票價值表的可執行面板,這些值每分鐘都要更新。 - 使用Portlet間通信(Inter- Communication,IPC)而不能刷新整個Web頁面。
例子:一個列出股票名稱的portlet需要更新另一個用于簡要描述這些股票的當前狀態(比如股票的當前價格、最高價格和最低價格)的portlet。 - 頁面包含大量通常為靜態的數據,這樣頁面操作只需要置換少量數據。
例子:Google地圖。 - 一個portlet需要基于在此portlet中的其他地方所做的選擇來檢索一個有限的數據集。
例子:一個表單有3個組合框:State、City和ZIP。當用戶選擇一個州時,該州所有的城市名稱都將出現在City組合框中。然后,用戶從City組合框中選擇一個城市,接著ZIP組合框中就會顯示該城市所有有效的郵政編碼。
Ajax技術的核心是Web瀏覽器對一些負責提供信息的Web服務的調用。如何為該解決方案設計架構呢?有3種值得考慮的架構。第一種架構使用Web瀏覽器作為集成點,出于隨之而來的安全考慮和瀏覽器方面的問題,該架構存在問題。第二種解決方案使用代理來獲取分布的資源,這消除了安全問題,但是添加了一個單點故障。第三種架構使用企業服務總線(enterprise service bus,ESB)來消除單點故障,并為Web服務和Ajax技術的蓬勃發展提供了最適宜的環境。
以瀏覽器為中心
在以瀏覽器為中心的架構中,Web瀏覽器成為聯系遠程系統的中心點,如圖1所示。數據從各個遠程系統中獲得,然后在瀏覽器處的JavaScript中進行整理和排序。

圖 1.以瀏覽器為中心的架構
這種架構是最自然的設計,它具有多處設計缺陷:
- JavaScript是一種糟糕的集成語言/環境。Java在各個方面都要比它好得多。
- 瀏覽器必須通過每個系統的身份驗證,而且可能使用不同的方法。
- 緩存靜態信息的能力很差,這會導致整個系統出現很長的延遲,這首先就失去了使用Ajax方法的意義!
如圖2所示,代理服務消除了第一種架構的所有缺陷,但是它也有自己的不足之處。它導致出現了一個系統單點故障。當然,Web服務器可以作為帶有硬件負載平衡器的集群的一部分。這無疑可以解決這個問題。

圖 2. 代理服務架構
盡管這種架構比起以瀏覽器為中心的架構已經有了巨大的改進,還可以做得更好。我們已經為Web開發人員提供了一個非常好的環境,但是這很可能為后端系統開發人員帶來不便。對遠程系統進行身份驗證以及數據整理仍然是必要的,但是至少后端程序員應該有更多可使用的工具來迎接挑戰。
企業服務總線
圖3與前面的兩幅圖又有所不同,因為企業服務總線(ESB)架構是一種邏輯架構。服務可以位于網絡上的任意位置,數據源可以被抽象到(可插入到ESB中的)服務中。ESB為后端系統開發人員處理了他們通常必須做的工作,比如服務身份驗證、數據轉換、協議轉換和可靠性特征。ESB可以擴展到帶有硬件負載平衡器的計算機集群上,這提供了代理服務架構所具有的優點。

圖 3.企業服務總線
為了說明在門戶中對Ajax應用程序使用ESB的優點,考慮對于每次Ajax調用都要引用一個Web服務的情況。對于小型門戶來說,Web服務的數量可能相對較少,譬如說幾十個。但是隨著門戶的增長,將會引入更多的Web服務。大量Web服務的添加與Ajax沒有關系,但是與SOA的實現有著密切的聯系。這是一個ESB的經典用例。盡管從嚴格意義上來說,它對于Ajax實現的操作不是必要的,但是它還是帶來了幾個好處:
- ESB消除了對ProxyServlet類(稍后將會介紹)的需求,因為代理服務更好地完成了同樣的工作。
- ESB支持監控和管理單個Web服務,同時無需在每個Web服務中增加額外的代碼。這包括監控服務水平協議(SLA)和報告違反情況。
- ESB可以保護Ajax代碼不會被外部提供者修改。如果外部提供者以一種無需修改門戶用戶界面但需要修改Ajax調用代碼的方式修改了WSDL,那么就有可能在ESB的代理服務中修改配置,而不用修改Ajax代碼。為什么這樣做比修改Ajax代碼更好呢?我認為,在ESB中代理服務的配置中進行小的改動比起在JavaScript中對每個到相關服務的引用進行修改更不容易出錯。此外,對于同樣的信息(比如股票報價查詢)還可以使用多個外部提供者,但是每個提供者具有不同的調用接口并返回XML。如果某個提供者出現故障或者只是為了進行負載平衡,使用多個提供者就很有利了。在ESB中可以輕松實現這一點,但是在JavaScript中這幾乎是不可能的。
對門戶使用的影響
當在門戶內采用Ajax編程方法時,出現了幾個問題。這些問題中的大多數與門戶的生命周期以及如何/何時挑選用戶信息有關。具體來說,門戶要使用諸如用戶在哪里單擊之類的信息來決定向其顯示何種類型的相關信息。WebLogic Portal具有一種叫做campaign的特性,它允許門戶設計人員基于用戶個人信息指定對用戶的有目的廣告宣傳。用戶個人信息中包括用戶的頁面歷史,即,用戶過去點擊過的頁面。門戶在頁面刷新時收集這類信息,所以如果用戶從未刷新頁面,門戶就無法(容易地)自動收集用戶信息。
考慮Ajax編程可能對門戶產生的以下副作用:
- 不可跟蹤性使campaign得不到有效使用(參見上面的內容)。
- 頁面刷新重置了DOM樹,所以當用戶最終刷新頁面時,所有對使用Ajax修改過的portlet的更新都會丟失。對portlet內容的修改將會丟失,還原為初始的頁面狀態。
- JSP中的所有JavaScript都是整個頁面共用的。考慮一個包含在每個portlet中的JavaScript片斷。當呈現最終的HTML頁面時,JavaScript片斷將在包含它的每個portlet中重復使用。類似地,如果已經將兩個portlet嵌入了JavaScript,而且每個portlet的腳本都有一個名為getData()的方法,那么最終的HTML頁面將會有兩個不同的getData()函數定義。調用該函數時很可能導致調用不正確的方法。如果有兩個名為isOK()的變量,也會出現同樣的情況。
一種最佳實踐是在所有腳本中使用獨有的變量和函數名稱,具體做法是在每個變量和函數的名稱前加上包含它們的portlet的名稱。
瀏覽器戰爭尚未結束,專有瀏覽器和基于標準的瀏覽器之間的戰爭仍在延續。Ajax相當有趣的一點是,其中有一半是標準(XHTML、XSLT、JavaScript/ECMAScript、DOM和Web services)驅動的。但是其核心技術——XmlHttpRequest對象——來自微軟。
下面列出了在進行跨瀏覽器的Ajax編程時要注意的一些重要的常見錯誤,以及如何避免這些陷阱。
安全性
XmlHttpRequest對象直接把瀏覽器連接到一臺遠程主機,要么是加載頁面的Web服務器,要么是從另一個完全不同的服務器。正如您所想像的,這里為惡意軟件提供了大量的機會。例如,一段惡意的JavaScript可能等著用戶輸入口令字段,然后把口令傳遞給一個遠程瀏覽器,而用戶卻不知道,甚至還沒有單擊頁面上的提交按鈕。如果把口令換為信用卡號碼,事情就變得更加有趣了。
為了避免這種風險,Mozilla拒絕到為Web頁面提供服務的主機之外的任意主機的連接。用戶不會看到錯誤消息,因為它根本就不會運行!
Internet Explorer (IE)采用另一種方法。當要求連接到遠程主機時,將使用一個對話框通知用戶,而用戶可以決定執行什么操作。但是要注意,該對話框不會告訴用戶要連接到哪個站點,所以用戶缺乏可以用于做出決策的信息。

圖 4. 沒有有用的信息!
這個問題的解決方案是使用一個Java servlet作為到Web服務的代理。該servlet獲得所有的參數,并把它們傳遞給遠程服務,接著將響應返回給Web站點。通過讓servlet運行在創建Web頁面的Web服務器上,Mozilla就會認為服務是本地的。注意,這是企業服務總線(比如AquaLogic Service Bus)的一個絕好用例。
使用XmlHttpRequest對象
包含XmlHttpRequest對象的XmlHttp庫最初是隨Internet Explorer 4一起發行的ActiveX控件。Mozilla包含一個兼容的函數庫,所以沒有什么好擔心的。我仍然推薦使用一個開源庫,比如Sarissa或DWR。然而,它們在將XML數據傳遞到對象的方式上存在著細微的區別。
更新DOM節點
Mozilla和IE之間最令人惱火的區別就是Web頁面中對DOM(Document Object Model,文檔對象模型)的處理。大多數函數的工作方式是一樣的(至少在DOM Level 2上),但是仍然有很多值得注意的地方。下面給出兩個例子。
innerHtml與innerText的使用
當使用新的動態內容更新<div>標簽時,IE用戶有兩個選擇:可以更新節點的innerText或innerHTML。二者的區別很細微,但是卻能在信息的顯示方面造成很大的差別,尤其是當要顯示的文本是XML格式或者包含HTML實體(比如尖括號或&符號)時。使用innerHTML時假定內容與標簽是一起放入的(不管內容是什么)。假設我們試著把以下文本放入一個節點中:
var txt = <b>This is a test</b> document.getElementById('mydivtext').innerText = txt; document.getElementById('mydivhtml').innerHTML = txt;
<div id="mydivhtml">部分在瀏覽器中看起來如下:
This is a test
<div id="mydivtext">部分看起來則是下面這樣:
<b>This is a test</b>基本上,innerText節點對輸入字符串進行轉義,這樣顯示在用戶面前的就是內容原來的樣子。但是Mozilla在DOM節點上不支持innerText屬性,所以更好的方法是用戶親自對文本進行轉義,并始終使用innerHTML。Sarissa有一個幫助器函數用于實現這一項功能:
document.getElementById('mydivtext').innerHTML = Sarissa.escape("<b>This is a test</b>")我們將得到同樣的結果,如下:
document.getElementById('mydivtext').innerText = "<b>This is a test</b>")在門戶中使用獨有名稱
當某個門戶頁面由WebLogic Portal(或任何門戶)進行解析時,每個portlet均作為完整的Web頁面放在HTML文檔中,包括<body>標簽(有時甚至包括<html>標簽)。因此,如果在每個portlet中始終以相同的ID來命名<div>標簽(理論上來說,這似乎是使編程標準化的一種良好方法),那么將獲得不正確的結果。考慮如果有兩個ID為“result_data”的元素,那么解析后的門戶頁面將會是什么樣子。
// Outer portal shell <html> // First portlet ... <div id="result_data"></div> ... // Second portlet ... <div id="result_data"></div> ... </html>現在,進行一次如下的調用:
document.getElementById('result_data').innerText = "stuff";哪個元素將被更新呢?答案基本上會隨瀏覽器的不同而不同,但是一般的答案就是“第一個”。
因此,這里的最佳實踐是使用portlet名稱作為HTML標簽中所有ID的前綴。
結束語
傳統的Web應用程序已經無法滿足客戶的要求。由于Google之類的公司提供了更新、更快和交互性更強的Web站點,客戶的期望值變得越來越高。構建使客戶可以提高工作速度和效率的用戶界面始終是一項戰略性挑戰。
Ajax編程是Web應用程序交互方面的新的事實標準,它為緊密耦合的數據和應用程序筒倉提供了部分解決方案。特別是在與BEA WebLogic平臺結合使用時,Ajax代表了Web編程的未來方向,并預示著構建具有高度的交互性和響應靈敏度的Web站點的新潮流。
在本系列的第二部分中,我將使用具體的例子說明如何在WebLogic Portal環境中使用Ajax。具體來說,我將演示大量portlet,以說明如何從JavaScript調用Web服務以及使用結果更新頁面、如何實現代理servlet來處理對外部Web服務的調用、如何使用Ajax把Web頁面嵌入到另一個Web頁面中,以及如何更新您自己的可更新數據庫表小構件。