第三個範例:ZK 與 Session
The following examples had been tested on Mozilla's Firefox and Microsoft's
IE. The document is provided as is. You are welcomed to use it for
non-commercial purpose.
Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu
請勿轉貼
看其他教材
ZK 跟所有 Web-based 的資訊系統一樣,網頁跟網頁之間都是互為獨立的
(稱之為 stateless),可是在實務上,往往網頁與網頁之間是有順序關係的;
例如如果使用者沒有登入,那麼使用者就不能修改使用者的註冊資料。
為了能夠達到這個目的,一般 Web-based 的資訊系統都利用 Session 的作法,
讓網頁與網頁之間可以藉由 Session 物件的一些共享資料來達成。在 ZK 中,
它也提供了類似的 package,這個 package 的名稱為 org.zkoss.zk.ui.Session;
除了這個範例之外,讀者可以參考該 package 的其他類別以及用法。
在這個範例中,我們假設設計一個系統,該系統有多個網頁,而要使用該系統,
使用者必須先執行 login.zul 網頁;一旦登入成功之後,使用者就能使用
其他網頁;為了簡化起見,我們只設計了一個 zul 網頁 todo1.zul(該網頁
是 todo.zul 的簡化版),其他網頁可以比照辦理。如果使用者未登入便想
直接存取 todo1.zul,則使用者會被導向 login.zul。
首先,讓我們設計 login.zul 的畫面,其畫面如下:
在畫面,請留意左上角的紅色框框,這是設定網頁標題(也就是類似 HTML 的
<title> 標籤的用法)的名稱。這個畫面的定義如下:
01 <?xml version="1.0" encoding="Big5"?>
02 <?page title="待辦事項系統"?>
03 <window title="登入畫面" border="normal" width="220px" mode="modal"
04 xmlns:h="http://www.w3.org/1999/xhtml">
05 <h:table>
06 <h:tr>
07 <h:td>帳號:</h:td><h:td><textbox id="user"/></h:td>
08 </h:tr><h:tr>
09 <h:td>密碼:</h:td><h:td><textbox type="password" id="pwd"/></h:td>
10 </h:tr><h:tr>
11 <h:td colspan="2" align="center"><button label="登入"/></h:td>
12 </h:tr>
13 </h:table>
14 </window>
網頁標題是由第 02 行的 page 標籤(其實是一個 PI)所設定的。另外,為了
能夠將帳號、密碼、以及登入按鈕依照表格的方式(也就是 HTML 的 <table>)
來完成,我們必須宣告一個名稱空間來分開 ZK 的標籤(也就是預設名稱空間)
以及 XHTML 的標籤。(請注意: 本範例故意使用 ZK 與 XHTML 在同一個
zul 網頁的方式來說明;否則,同樣的排版也可以利用 ZK 的 <grid> 來
完成)。
從原始碼的綠色部分,讀者應該可以看得出來,我們定義了一個簡稱為 h 的名稱
空間,而該空間為 http://www.w3.org/1999/xhtml(即 XHTML)。然後,所有的
XHTML 標籤名稱之前都需要加上 h:;以本範例來說,需要加上 h: 包含
h:table、h:tr、h:td 等。
在使用者輸入帳號和密碼之後,使用者會點一下"登入"按鈕;一旦"登入"按鈕
被點選,login() 方法會被執行。該事件的註冊方式如下:
<button label="登入" onClick="login()"/>
login() 方法的工作包含:從輸入欄位取得資料,並將其資料與系統
內使用者的身分做驗證;如果驗證成功,程式會在 Session 物件設定包含
必要的資料(例如帳號、密碼等),然後執行下一個網頁 todo1.zul;否則,
程式會將使用者再度導向 login.zul。登入的程式碼如下:
01 import org.zkoss.zk.ui.*;
02
03 void login() {
04 // 也可以連資料庫驗證
05 if(user.value.equals("123") && pwd.value.equals("123")) {
06 Session s = Sessions.getCurrent();
07 s.setAttribute("user", user.value);
08 s.setAttribute("pwd", pwd.value);
09 Executions.sendRedirect("todo1.zul");
10 } else {
11 Executions.sendRedirect("login.zul");
12 }
13 }
由於我們需要 ZK 的 Session 物件,因此需要第 01 行。在驗證使用者的
身分資料部分,雖然可以使用資料庫,但是為了簡化起見,我們利用第 05 行
的方式來檢查;如果驗證失敗,程式會將使用者導向 login.zul 網頁,如
第 11 行所示,其中 Executions.sendRedirect("URL"); 會將
網頁導向 URL;反之,如果驗證成功,程式首先在第 06 行取得目前的
Session 物件,並且在第 07 和 08 行利用該物件的 setAttribute() 方法
來設定兩個 Session 共享的資訊,其分別是帳號(屬性名稱為 user;屬性值
由 user 欄位取得)和密碼(屬性名稱為 pwd; 屬性值由 pwd 欄位取得)。
最後,將網頁導向 todo1.zul。
在結束說明 login() 之前,還有一個很重要的用法要說明一下。在第 05 行
程式必須判斷帳號以及密碼都等於 123,因此 if 敘述內必須是"且"的運算子,
在 Java 中它應該是 &&,但是因為該程式碼在 zul(或者更明確的
說,XML)網頁內,所以該運算子不能寫成 && 而必須寫成 &&,這樣一來,ZK 處理器在剖析 login.zul 的時候,會將它轉成 &&
然後再進行編譯。
todo1.zul 是第二個範例的簡易版,其畫面的定義部分如下:
<?xml version="1.0" encoding="Big5"?>
<window title="待辦事項清單" width="640px" border="normal" closable="true"
onClose="self.visible=false;logout();">
<listbox id="box" multiple="true" rows="4">
<listhead>
<listheader label="待辦事項" />
<listheader label="重要性" width="50px" />
<listheader label="日期" width="90px" />
</listhead>
</listbox>
</window>
有了這樣的定義,其畫面如下:
絕大部分的原始碼之前已經說明了,就不再多說。其中比較不同是視窗的右上角
出現了一個像一般視窗的 "X" 按鈕。我們希望在使用者點選 "X" 之後,該視窗
會不見,而且就像登出一樣,它會將使用者導向 login.zul;因此,我們幫
<window> 的 closable 屬性設定為 true(使得出現 "X");若使用者
在 "X" 點一下,則會呼叫 onClose 事件的處理程式,這包含 self.visible=false;
(這會讓視窗隱藏起來),並呼叫 logout() 方法。
另外,除了 login.zul,所有同一個系統中的其他網頁應該在載入的時候,就應該檢查
Session 中的共用資料(在本範例中即屬性名稱為 user 以及 pwd 是否存在,
必要的話,還可以檢查屬性的值是否存在)。為了做這兩件事,程式碼如下:
01 import org.zkoss.zk.ui.*;
02
03 Session s = Sessions.getCurrent();
04 if(s.getAttribute("user") == null)
05 Executions.sendRedirect("login.zul");
06
07 void logout() {
08 s.invalidate();
09 Executions.sendRedirect("login.zul");
10 }
從程式碼中可以看出:第 03 行取得目前的 Session 物件,然後在第 04 行
檢查是否定義了 user 屬性(也可以在加上 pwd 屬性的檢查);如果未定義,
導向 login.zul;反之,則繼續執行本網頁。
logout() 除了會將使用者導向 login.zul 之外,並且會再重新導向之前
先把 Session 物件解除掉(利用 invalidate() 方法)。除了利用 "X" 之外,
當然也可以使用其他的方式登出;但是不論使用哪一種方式,記得一定要
把 Session 物件 invalidate 掉,該物件才不會被不當的使用。
我們鼓勵讀者試著從各種方式來使用 Session。例如:開啟一個新的網頁之後,
馬上試著連 todo1.zul;或者 logout 之外,直接更改 URL 試圖連接 todo1.zul。
有了良好的 Session 控制,這些方式都不應該可以連上 todo1.zul;除了
正確的登入。
在測試的過程中,你或許會發現雖然在無法順利使用 todo1.zul 的情形下,
使用者還是有一個短暫的時間可以看到 todo1.zul 的畫面,如果該畫面有
一些機密內容,那麼 todo1.zul 的設計還是不妥。為了解決這個問題,我們
可以在一開始讓 <window> 是看不到的,只有使用者正確的登入後
才讓 <window> 能夠顯示出來。由於程式碼還蠻簡單的,我們就不再說明,
而把整個程式碼的內容列示出來:
<?xml version="1.0" encoding="Big5"?>
<window id="win" title="待辦事項清單" width="640px" border="normal" closable="true" visible="false"
onClose="self.visible=false;logout();">
<zscript>
import org.zkoss.zk.ui.*;
Session s = Sessions.getCurrent();
if(s.getAttribute("user") == null)
Executions.sendRedirect("login.zul");
else
win.setVisible(true);
void logout() {
s.invalidate();
Executions.sendRedirect("login.zul");
}
</zscript>
<listbox id="box" multiple="true" rows="4">
<listhead>
<listheader label="待辦事項" />
<listheader label="重要性" width="50px" />
<listheader label="日期" width="90px" />
</listhead>
</listbox>
</window>
練習題: 請修改 login.zul 使得使用者在輸入完密碼之後,能夠直接按
"Enter" 來執行登入,而不必一定要用滑鼠來點選"登入"按鈕來登入。
Last Updated: Monday, 30-May-2011 11:33:45 CST
Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu