JSP 入門:Session
All examples are solely used for educational purposes. This document is provided as is. You are welcomed to use it for non-commercial purpose.Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu 在以下的範例中,我們假設你已經在你自己的電腦上面安裝好了 Apache Tomcat 而且 port 為 8080。如果你還沒安裝好,你可以 參考 安裝 Tomcat 5.5.x。
請勿轉貼
看其他教材
開發網頁資訊系統(web-based information systems)的人員都應該知道一件事實, 那就網頁與網頁之間是獨立;也就是說,若有兩個網頁 A 和 B,呼叫 A 網頁是一件 獨立的作業,而呼叫 B 網頁又是一個獨立的作業,兩者之間是沒有關係的,網頁 伺服器無法判斷這些網頁是由同一個使用者、或者多個使用者呼叫的;比較專業的說法, HTTP 協定是無狀態的(stateless)(或者翻譯成"不會保留狀態的"比較恰當)。 但是,這種無狀態的協定,在應用系統的使用上卻是非常不方便的; 例如,某些網頁需要知道使用者是否已經登入;若已經登入才可以看網頁的內容, 否則要求使用者登入。一般來說,有三種常用方式可以讓 web server 經由某些 資料來判斷,而這三種方法分別為隱藏資料在網頁中、使用 Cookie、和 Session。 這三種方式中,以 Session 的方式相對安全。
在以下範例中,總共有四個網頁,分別是 Session1.jsp、Sessionw1.jsp、Session2、以及 logout.jsp。Session1.jsp 中包含了 HTML 表單的元件,這些元件允許使用者 選擇某一種程式語言,然後把使用者選擇的語言傳給 Session2.jsp;Seesion2.jsp 會依據 Session1.jsp 傳過來的語言,提供建議書單;最後,使用者點選"結束"來 完成工作。如果使用者利用"加入最愛"(或者加入書籤),然後試圖不經過 Seesion1.jsp 而直接執行 Session2.jsp,Session2.jsp 會直接將使用者導向 Session1w.jsp。
為了完整說明 session 的特性,我們利用三種實例來說明,在這些情境中,我們 特別列印 session 的 id(具有唯一性)到 tomcat 的 console 上,其畫面如下:
- 首先,我們先說明實例 II:實例 II 代表正常的使用
方式,也就是使用者經由 Session1.jsp (類似登入畫面)開始使用,Session1.jsp
的畫面如下:
<%@page contentType="text/html;charset=Big5" %> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=Big5"> <title> Test Servlet Sessions </title> </head> <body> <% String id = session.getId(); // 我們自訂 Session 的屬性名稱以及屬性值 session.setAttribute("sid", id); %> <form method="POST" action="http://localhost:8080/test/Session2.jsp"> <strong>請選擇你想進一步了解的電腦語言:</strong><p/> <input type="radio" name="lang" value="C">C</input><br/> <input type="radio" name="lang" value="C++">C++</input><br/> <input type="radio" name="lang" value="Java" checked>Java</input><br/> <input type="radio" name="lang" value="VB">Visual Basic</input><p/> <input type="submit" value="確定"/> <input type="reset"/> </form> </body> </html>
session 可以翻譯成"會期";何謂"會期"?從拿起電話、撥電話、對話、一直到掛掉 電話,稱之為一個 session;又,使用者從登入開始、進行資料處理(例如,購物、 填寫問卷等)、一直到登出,這也是一個 session。由於 HTTP 是無狀態的,可是 一個 session 卻常常會使用一連串、多個網頁呼叫,而各個網頁之間又會傳遞一些 共用的資料,這時候就需要一個物件來幫忙儲存這些資料,而這個物件在 JSP/servlet 中就是 HttpSession 的物件。
如果希望將某一特定資料(例如,帳號/密碼、session ID 等)從 A 網頁傳到 B 網頁, 由於 A 網頁和 B 網頁都是屬於同一個 session,那麼我們就可以為將該資料取一個 名稱(在本範例稱之為 sid),然後利用 setAttrbute(名稱, 資料) 在 A 網頁將該資料儲存到 session 中;之後,在必要的時候(例如在 B 網頁), 就可以利用 getAttribute(名稱) 取得該資料的值。
假設使用者在 Session1.jsp 的畫面,選擇了 "Java" 並點選了"確定" 按鈕。點選了之後,Session2.jsp 會被執行,而執行的結果如下:
<%@page contentType="text/html;charset=Big5" %> <%! private final static String names[] = {"C", "C++", "Java", "VB"}; private final static String books[] = {"C: How to Program", "C++: How to Program", "Java: How to Program", "Visual Basic: How to Program"}; %> <html> <head><title>Recommended Book List</title></head> <body> <% String id = (String) session.getAttribute("sid"); // 用來觀察 session ID 的變化,直接寫到 tomcat 的 console,請仔細觀察 System.out.println(session.getId()); if(session.isNew()) { // 用來觀察 session ID 的變化 System.out.println("NewSession:" + id); response.sendRedirect("Session1w.jsp"); } else { String lang = request.getParameter("lang"); if(lang == null) { // 用來觀察 session ID 的變化 System.out.println("lang:" + id); response.sendRedirect("Session1w.jsp"); } else { for(int i=0; i<names.length; i++) { if(lang.equals(names[i])) { out.println("<h1>" + books[i] + "</h1>"); break; } } } } %> <a href="logout.jsp">結束</a> </body> </html>
檢查使用者是否直接進入 Session2.jsp 的常見方式有兩種:一種是利用 Session1.jsp 傳過來的 sid 資料;如果使用者經過 Session1.jsp, 那麼 getAttribute("sid") 必然包含資料;反之,如果直接進入, 則 getAttribute("sid") 的回傳值為 null。在本範例中, 我們並未使用這種檢查方式(但是我們鼓勵讀者試試看),而是讓 sid 列印在 tomcat 的 console 上,以便追蹤。另一種檢查的方式是利用 session 的 isNew() 方法來檢查:若是新的 session,則回傳 true;反之,回傳 false。
如果使用者是直接進入,我們希望把他導回 Session1.jsp。為了完成這項工作, 在程式碼中我們使用了 response.sendRedirect("Session1w.jsp");, 這個用法以及設計的原因,我們一一描述如下:
- response 也是 JSP 的一個內建物件,它是用來將資料回傳給瀏覽器 用的。response 的 sendRedirect(URL) 的方法是將參數的 URL 傳給瀏覽器並指示瀏覽器向 URL 連結。
- 在 sendRedirect(URL) 中,我們將 URL 設定為 Session1w.jsp 而不是 Session1.jsp,這是因為我們經由實驗發現, 如果是 Session1.jsp 的話,就算我們在程式中加入了結束"會期"的 指令(也就是 invalidate()),"會期"是無法被適當的清除;但是如果 是除了 Session1.jsp 之外的網頁(如 Session1w.jsp),則 "會期"可以被清除乾淨。
- 請特別注意: 在 JSP 執行了 response.sendRedirect(URL); 之後,由於畫面會直接跳到指定的 URL,大多數人都以為 JSP 程式的執行也已經結束; 但是,事實上卻不是如此,tomcat 會繼續把剩餘的 JSP 程式碼執行完。請試著在 紅色原始碼之後加上 System.out.println("還在執行");,然後觀察 tomcat console 上的訊息,你就可以確認這項說明。因此,我們建議最好在每一個 response.sendRedirect(URL); 之後,馬上加入 return; 的 敘述。我們沒有加的原因是,在這個範例中,response.sendRedirect(URL); 是最後一個被執行的敘述。
等到使用者確認了推薦圖書之後,正確的作法就是點選"結束"。請注意: 如果使用者沒有點選"結束",這個"會期"是不會立即結束的;除非,使用者關閉整個 瀏覽器(僅關掉瀏覽器的 TAB 似乎沒有辦法結束"會期"),或者等待 30 分鐘(這個 時間是 tomcat 的預設值)。 點選"結束"會執行 logout.jsp,其畫面以及原始碼分別列示如下:
<%@page contentType="text/html;charset=Big5" %> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=Big5"> <title> Test Servlet Sessions </title> </head> <body> <% String id = (String) session.getAttribute("sid"); System.out.println("logout:" + id); session.invalidate(); %> 你已經登出,<a href="Session1.jsp">重新選擇程式語言</a>。 </body> </html>
- 實例 III:假設這個時候,使用者直接執行 Session2.jsp(可以是直接
輸入 URL 或者之前已經存在"我的最愛"),這時候會發生什麼事?
- 首先,程式會試圖擷取 sid 的值;但是由於之前的 session 已經 被結束掉了(執行了 invalidate()),所以 sid 的值不 存在,因此 id 的值為 null。
- 由於舊的"會期"已經結束了,因此在預設的情形下,tomcat 會產生一個新的 "會期",所以 session.getId() 會回傳一個新的 ID;請比較圖中 II 和 III 的 ID, 兩個 session 的 ID 並不相同:一個是 "A2AE13445....",另一個是 "8CF60C5...", 這代表一個新的"會期"已經產生了。
- 由於一個新的"會期"產生了,session.isNew() 回傳 true;又由於 id 的值為 null,所以 tomcat 的 console 上會列印 "NewSession:null",這也可以 從圖中看出。
- 最後,由於我們不希望使用者直接進入 Session2.jsp,所以 我們把網頁導向 Session1w.jsp。
<%@page contentType="text/html;charset=Big5" %> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=Big5"> <title> Test Servlet Sessions </title> </head> <body> <% String id = (String) session.getAttribute("sid"); System.out.println("Waring:" + id); session.invalidate(); %> 你必須先選擇語言,<a href="Session1.jsp">請選擇</a>。 </body> </html>
- 實例 I:實例 I 和實例 III 類似,其中唯一的差別就是執行的 時機:實例 III 代表在結束一個"會期"後,直接執行 Session2.jsp 的情形;而實例 I 代表在一開始沒有任何舊的"會期"的情形下,直接執行 Session2.jsp 的情形。從觀察圖中的結果,我們可以清楚的看出每一個新"會期" 會有一個唯一的 ID;如果"會期"結束,那麼為之前"會期"設定的屬性也會被 清除掉。
Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu
沒有留言:
張貼留言