官术网_书友最值得收藏!

1.4 第一個Ajax應用實例

1.4.1 傳統的聊天室

B/S結構的聊天室要實現的功能有兩個:第一個功能是對用戶的管理,包括用戶登錄和用戶注冊等;第二個功能是管理用戶的聊天信息,系統需要保持用戶最近的聊天信息。

通常情況下,系統會將用戶信息、聊天信息都保存在數據庫里。本應用為了簡化,用戶信息以Properties文件進行保存,用戶聊天信息保存在內存中(使用一個List保存)。

該B/S聊天室遵循MVC的開發模式:客戶端向控制器發送請求,控制器負責攔截用戶請求,調用Model處理用戶請求,控制器根據Model的處理結果,決定向用戶呈現怎樣的界面。B/S聊天室的業務邏輯非常簡單,包含如下功能。

(1)用戶注冊:向保存用戶名、密碼的文件中增加一條記錄。

(2)用戶登錄:判斷用戶輸入的用戶名、密碼是否正確,若正確則會跳轉到聊天頁面,否則不跳轉。

(3)用戶聊天:發送消息讓所有用戶看到。

聊天室的組件關系如圖1-6所示。

圖1-6 聊天室的組件關系

1.實現業務邏輯組件

系統沒有采用數據庫存放用戶信息,而是使用Properties文件存放用戶名和密碼。所有的用戶登錄驗證、新用戶注冊都需要通過Properties文件校驗。業務邏輯組件提供如下方法用于加載屬性文件。

上面的程序代碼用于實現讀取userFile.properties文件中的用戶名和密碼信息。這個方法是個工具方法,用于加載所有用戶名和密碼。userList保存了當前系統中所有用戶名和密碼,它是ChatService對象的實例屬性,是一個Properties對象,其中屬性名是用戶名,屬性值是密碼。

如果系統的注冊用戶非常多,則屬性文字非常大,userList也將非常大,這可能導致系統的性能下降,因此采用數據庫保存信息更加合適。本例只為演示用,因此沒有采用數據庫。

此外,還有對應的方法用于將userList保存到Properties文件中,每次用戶注冊成功后都應該將新注冊的用戶保存到Properties文件中。保存userList的方法如下:

上面的粗體字代碼用于將userList對象中的用戶名、密碼信息保存到userFile.properties文件中。

上面的兩個方法都是系統進行持久化的方法,只不過此處的持久化無須訪問數據庫,而只是使用Properties文件來保存持久化信息。業務邏輯對象必須向控制器提供的方法如下:

(1)boolean validLogin(String user,String pass):用于判斷用戶名和密碼是否可以成功登錄。

(2)boolean addUser(String name,String pass) :用于注冊用戶時向Properties文件中增加記錄。

(3)String getMsg():用于獲取系統所保存的所有用戶的聊天信息。

(4)void addMsg(String user,String msg):用于增加聊天信息。聊天信息是瞬態信息,系統沒有對聊天信息完成持久化,但每個用戶的發言應該被增加到聊天信息。

本聊天系統的業務邏輯組件直接依賴上面的工具方法進行持久化,所以無須依賴持久化組件。業務邏輯組件ChatService的代碼如下:

2.實現控制器

系統的控制器由Servlet充當,Servlet負責攔截用戶請求,然后調用ChatService對象處理用戶請求,根據處理結果,將請求forward到合適的頁面顯示。本系統包含三個用例:用戶注冊、用戶登錄和用戶聊天。系統為每個請求配置一個控制器。控制器的運行結構大致相似,下面以注冊所用的控制器為例進行講解。

如上面程序所示,該RegServlet調用ChatService對象的addUser()方法來注冊新用戶,也就是控制器調用業務邏輯組件方法來處理用戶請求。

其余兩個控制器ChatServlet和LoginServlet與此類似,ChatServlet調用addMsg()和getMsg()方法來添加聊天信息和顯示聊天信息。兩個控制器調用getMsg()方法獲取聊天記錄后,將聊天記錄放置到HttpServletRequest的msg屬性中。JSP頁面則直接通過如下的表達式語言來輸出聊天信息:

聊天界面由一個文本域和一個文本框組成,文本框負責收集用戶輸入的聊天信息,文本域負責顯示當前所有用戶的聊天信息。聊天頁面的代碼片段如下:

上面代碼中文本區用于顯示系統的聊天信息,下面表單中文本框和按鈕用于輸入聊天信息和發送聊天信息。除此之外,該頁面也使用了JavaScript來提供客戶端輸入校驗,詳細代碼可查看源碼。

前面的程序已經實現了一個簡單的B/S聊天室,但這個B/S聊天室存在一些小問題。傳統B/S結構的應用都是基于請求/響應的應用。客戶端向服務器發送請求,而服務器則生成對客戶端的響應。在這種結構模式里,服務器不會主動向客戶端發送響應。如果客戶端不發送任何請求,則即使系統的聊天信息發生改變,用戶也依然看不到其他用戶的聊天信息。

當用戶發送請求時,請求被控制器截獲,控制器處理完用戶請求后,將請求轉發到JSP頁面,由該JSP頁面呈現處理結果。關鍵問題就在這里:每次用戶發送請求后只能等待服務器響應,如果服務器響應很慢,客戶端瀏覽器就將一直等待,什么事情也做不了。如果客戶端想再次發送請求,則完全不可能,因為服務器沒有生成響應,即客戶端的瀏覽器是一片空白。

當用戶發送聊天信息時,客戶端瀏覽器需要不斷地下載聊天頁面,即每次發送聊天信息后,都需要重新下載頁面。

服務器每次響應都會生成一個完整的頁面。在實際應用中,完整頁面包含的內容相當多,少則幾百行,多則幾千行、上萬行。有時除了少量的數字和文字需要改變,頁面的其他修飾、效果、圖片等都無須更新,但客戶端必須重新下載這些已經下載過的資源。相同資源的大量重復下載,嚴重占用了客戶的網絡帶寬,也使得客戶端的速度變慢。總體來說,傳統的B/S聊天室有如下問題。

(1)JSP頁面無法異步發送請求,用戶請求與服務器響應嚴格交替:用戶請求→服務器響應。如果用戶沒有發送請求,服務器就不會響應;如果服務器響應沒有完成,用戶就無法再次發送請求。

(2)服務器響應后總是生成完整JSP頁面,導致大量下載重復資源。

1.4.2 使用Ajax實現聊天室功能

針對傳統的B/S聊天室所存在的問題,Ajax技術進行了相應的改進。Ajax技術并不是要取代B/S結構的應用,而是更好地完善了傳統的Web應用。

對于JSP存在的問題,Ajax都有非常好的解決方案:Ajax使用XMLHttpRequest異步發送請求,Ajax的服務器響應僅是必須更新的數據,而不再是整個頁面。JavaScript負責將必須更新的數據加載到視圖頁面中。

使用Ajax可提高頁面的復用:通過使用Ajax技術,請求和頁面分離開,一個視圖頁面可以發送多個請求,因而用戶可以長時間使用同一個頁面,故可以更好地復用一個已下載的頁面。

1.異步發送請求

異步發送請求是Ajax最核心的內容,Ajax中的A就代表Asynchronous(異步的),Ajax使用XMLHttpRequest對象異步發送請求。在某種程度上,Ajax是以XMLHttpRequest對象為核心,結合JavaScript、DOM、CSS后組成的新技術。

為了使用XMLHttpRequest對象,必須先創建XMLHttpRequest對象,創建該對象的代碼如下:

上面程序中的代碼可以在Internet Explorer、Firefox、Opera(除IE之外的其他瀏覽器都會遵守DOM2規范)等瀏覽器中創建XMLHttpRequest對象。因為XMLHttpRequest在不同的瀏覽器中實現方式不同,因而在不同的瀏覽器中創建XMLHttpRequest對象的方式也略有差異。

一旦XMLHttpRequest對象創建成功,系統就可以使用XMLHttpRequest發送請求。XMLHttpRequest請求與傳統的請求不同,傳統的發送請求需要提交表單,或者請求新的網絡頁面,這都將導致瀏覽器重新發送請求、重新加載新頁面。而XMLHttpRequest發送請求則通過JavaScript代碼完成,這就避免了頁面的刷新,這也是異步發送請求的核心。

XMLHttpRequest對象包含send()方法用于發送請求。在發送請求之前,應先與請求的URL取得連接,XMLHttpRequest通過open()方法打開與請求URL的連接。下面是使用XMLHttpRequest發送請求的JavaScript代碼。

上面的程序用open()方法打開與請求資源的連接,因為本系統采用POST方法發送請求參數,因此在請求里增加了Content-Type請求頭,并將該請求頭的值設為application/x-www-form-urlencoded,這是為了保證對請求參數采用合適的格式發送。程序中的粗體字代碼是發送POST請求的完整過程。

一般而言,使用XMLHttpRequest發送請求的步驟如下:

(1)使用open()方法連接服務器URL。

(2)調用setRequestHeader()方法為請求設置合適的請求頭。根據不同的請求,可能需要設置不同的請求頭。

(3)指定回調函數。所謂回調函數就是用于檢測XMLHttpRequest狀態的函數(類似于事件監聽器),當XMLHttpRequest的狀態發生改變時,該回調函數將被觸發而自動執行。

(4)調用send()方法發送請求。

通過上面的程序可以發現,在采用Ajax發送請求時,發送請求比傳統Web應用略復雜。傳統Web應用發送請求有以下兩種形式:

(1)在瀏覽器的地址欄輸入請求地址后按Enter鍵發送GET請求。

(2)提交表單發送的方式比較簡單,基本無須編寫任何程序代碼,在改為使用Ajax請求后,需要先創建XMLHttpRequest對象,再使用該對象來發送異步請求。

2.使用Servlet生成響應

控制器Servlet將請求獲取,調用Service()方法完成信息處理,然后將結果發送到客戶端頁面,下面是這種用法下的控制器代碼。

上面的Servlet與前一個Servlet(1.4.1節)基本相似,只是在Servlet處理用戶請求結束后并未直接生成響應,而是輸出聊天信息到聊天頁面。

3.解析服務器響應

服務器響應生成簡單文本,而XMLHttpRequest包含屬性responseText,該屬性可獲取服務器響應生成的文本。在解析服務器響應之前,必須先判斷服務器響應是否完成,以及響應是否正確,如生成狀態碼為404等的錯誤響應也是沒有意義的。為此,XMLHttpRequest提供了以下兩個屬性。

(1)readyState:判斷服務器響應的狀態,其中4表明響應完成。

(2)status:判斷服務器響應對應的狀態碼,其中200表明響應正常,404表明資源丟失,500表明內部錯誤等。關于XMLHttpRequest的詳細介紹請參考第2章。

判斷完響應狀態后,可以使用responseText屬性獲取服務器響應文本,并將該文本輸出到頁面顯示。下面是解析、處理服務器響應的JavaScript代碼。

上面的程序中代碼先判斷XMLHttpRequest的響應狀態,當readyState屬性為4時表明響應完成;再判斷status是否為200,是則表明服務器生成了正確的響應。

此時,瀏覽器的頁面通過JavaScript與服務器進行的通信基本完成。客戶端通過sendRequest()函數向服務器發送請求,服務器通過chatServlet處理用戶請求,在服務器響應完成后且服務器生成了正確的響應后,客戶端通過DOM操作將服務器響應加載在視圖頁面上。

如果視圖頁面使用的是JSP頁面,也可以通過Servlet將請求轉發到JSP頁面生成響應。

整個聊天室HTML頁面的代碼如下:

通過上面的頁面,基于Ajax的聊天室已基本完成。Ajax聊天室的客戶端請求在后臺異步發送,客戶端讀取服務器響應也通過JavaScript完成。整個過程不會阻塞用戶的聊天,即使服務器的響應變慢,客戶端依然可發送請求或者查看原有的聊天記錄,無須等待下載頁面。圖1-7所示為該聊天室頁面的運行效果。

聊天頁面配置運行

圖1-7 聊天室頁面的運行效果

主站蜘蛛池模板: 新巴尔虎右旗| 泗阳县| 龙泉市| 屏山县| 呼和浩特市| 内乡县| 开远市| 葫芦岛市| 德庆县| 临沂市| 汤阴县| 彩票| 乌审旗| 楚雄市| 竹山县| 积石山| 鹿泉市| 资兴市| 平山县| 丹巴县| 云阳县| 准格尔旗| 临夏县| 临桂县| 玉林市| 仁布县| 邢台市| 湟中县| 卓尼县| 南澳县| 德清县| 武胜县| 西盟| 蒙阴县| 宿松县| 酒泉市| 湟中县| 万荣县| 罗平县| 中牟县| 武山县|