jsp Servlet基礎入門學習:工作階段狀態
最後更新:2017-02-28
來源:互聯網
上載者:User
js|servlet
10.1 工作階段狀態概述
HTTP協議的“無狀態”(Stateless)特點帶來了一系列的問題。特別是通過線上商店購物時,伺服器不能順利地記住以前的事務就成了嚴重的問題。它使得“購物籃”之類的應用很難實現:當我們把商品加入購物籃時,伺服器如何才能知道籃子裡原先有些什嗎?即使伺服器儲存了上下文資訊,我們仍舊會在電子商務應用中遇到問題。例如,當使用者從選擇商品的頁面(由普通的伺服器提供)轉到輸入信用卡號和送達地址的頁面(由支援SSL的安全伺服器提供),伺服器如何才能記住使用者買了些什嗎?
這個問題一般有三種解決方案:
Cookie。利用HTTP Cookie來儲存有關購物會話的資訊,後繼的各個串連可以查看當前會話,然後從伺服器的某些地方提取有關該會話的完整資訊。這是一種優秀的,也是應用最廣泛的方法。然而,即使Servlet提供了一個進階的、使用方便的Cookie介面,仍舊有一些繁瑣的細節問題需要處理:
從其他Cookie中分別出儲存會話標識的Cookie。
為Cookie設定合適的作廢時間(例如,停機時間超過24小時的會話一般應重設)。
把會話標識和伺服器端相應的資訊關聯起來。(實際儲存的資訊可能要遠遠超過儲存到Cookie的資訊,而且象信用卡號等敏感資訊永遠不應該用Cookie來儲存。)
改寫URL。你可以把一些標識會話的資料附加到每個URL的後面,伺服器能夠把該會話標識和它所儲存的會話資料關聯起來。這也是一個很好的方法,而且還有當瀏覽器不支援Cookie或使用者已經禁用Cookie的情況下也有效這一優點。然而,大部分使用Cookie時所面臨的問題同樣存在,即伺服器端的程式要進行許多簡單但單調冗長的處理。另外,還必須十分小心地保證每個URL後面都附加了必要的資訊(包括非直接的,如通過Location給出的重新導向URL)。如果使用者結束會話之後又通過書籤返回,則會話資訊會丟失。
隱藏表單域。HTML表單中可以包含下面這樣的輸入欄位:<INPUT TYPE="HIDDEN" NAME="session" VALUE="...">。這意味著,當表單被提交時,隱藏欄位的名字和資料也被包含到GET或POST資料裡,我們可以利用這一機制來維持會話資訊。然而,這種方法有一個很大的缺點,它要求所有頁面都是動態產生的,因為整個問題的核心就是每個會話都要有一個唯一識別碼。
Servlet為我們提供了一種與眾不同的方案:HttpSession API。HttpSession API是一個基於Cookie或者URL改寫機制的進階工作階段狀態跟蹤介面:如果瀏覽器支援Cookie,則使用Cookie;如果瀏覽器不支援Cookie或者Cookie功能被關閉,則自動使用URL改寫方法。Servlet開發人員無需關心細節問題,也無需直接處理Cookie或附加到URL後面的資訊,API自動為Servlet開發人員提供一個可以方便地儲存會話資訊的地方。
10.2 工作階段狀態跟蹤API
在Servlet中使用會話資訊是相當簡單的,主要的操作包括:查看和當前請求關聯的會話對象,必要的時候建立新的會話對象,查看與某個會話相關的資訊,在會話對象中儲存資訊,以及會話完成或中止時釋放會話對象。
10.2.1 查看當前請求的會話對象
查看當前請求的會話對象通過調用HttpServletRequest的getSession方法實現。如果getSession方法返回null,你可以建立一個新的會話對象。但更經常地,我們通過指定參數使得不存在現成的會話時自動建立一個會話對象,即指定getSession的參數為true。因此,訪問當前請求會話對象的第一個步驟通常如下所示:
HttpSession session = request.getSession(true);
10.2.2 查看和會話有關的資訊
HttpSession對象生存在伺服器上,通過Cookie或者URL這類後台機制自動關聯到請求的寄件者。會話對象提供一個內建的資料結構,在這個結構中可以儲存任意數量的鍵-值對。在2.1或者更早版本的Servlet API中,查看以前儲存的資料使用的是getValue("key")方法。getValue返回Object,因此你必須把它轉換成更加具體的資料類型。如果參數中指定的鍵不存在,getValue返回null。
API 2.2版推薦用getAttribute來代替getValue,這不僅是因為getAttribute和setAttribute的名字更加匹配(和getValue匹配的是putValue,而不是setValue),同時也因為setAttribute允許使用一個附屬的HttpSessionBindingListener 來監視數值,而putValue則不能。
但是,由於目前還只有很少的商業Servlet引擎支援2.2,下面的例子中我們仍舊使用getValue。這是一個很典型的例子,假定ShoppingCart是一個儲存已購買商品資訊的類:
HttpSession session = request.getSession(true);
ShoppingCart previousItems =
(ShoppingCart)session.getValue("previousItems");
if (previousItems != null) {
doSomethingWith(previousItems);
} else {
previousItems = new ShoppingCart(...);
doSomethingElseWith(previousItems);
}
大多數時候我們都是根據特定的名字尋找與它關聯的值,但也可以調用getValueNames得到所有屬性的名字。getValuesNames返回的是一個String數組。API 2.2版推薦使用getAttributeNames,這不僅是因為其名字更好,而且因為它返回的是一個Enumeration,和其他方法(比如HttpServletRequest的getHeaders和getParameterNames)更加一致。
雖然開發人員最為關心的往往是儲存到會話對象的資料,但還有其他一些資訊有時也很有用。
getID:該方法返回會話的唯一標識。有時該標識被作為鍵-值對中的鍵使用,比如會話中只儲存一個值時,或儲存上一次會話資訊時。
isNew:如果客戶(瀏覽器)還沒有綁定到會話則返回true,通常意味著該會話剛剛建立,而不是引用自用戶端的請求。對於早就存在的會話,傳回值為false。
getCreationTime:該方法返回建立會話的以毫秒計的時間,從1970.01.01(GMT)算起。要得到用於列印輸出的時間值,可以把該值傳遞給Date建構函式,或者GregorianCalendar的setTimeInMillis方法。
getLastAccessedTime:該方法返回客戶最後一次發送請求的以毫秒計的時間,從1970.01.01(GMT)算起。
getMaxInactiveInterval:返回以秒計的最大時間間隔,如果客戶請求之間的間隔不超過該值,Servlet引擎將保持會話有效。負數表示會話永遠不會逾時。
10.2.3 在會話對象中儲存資料
如上節所述,讀取儲存在會話中的資訊使用的是getValue方法(或,對於2.2版的Servlet規範,使用getAttribute)。儲存資料使用putValue(或setAttribute)方法,並指定鍵和相應的值。注意putValue將替換任何已有的值。有時候這正是我們所需要的(如下例中的referringPage),但有時我們卻需要提取原來的值並擴充它(如下例previousItems)。範例程式碼如下:
HttpSession session = request.getSession(true);
session.putValue("referringPage", request.getHeader("Referer"));
ShoppingCart previousItems =
(ShoppingCart)session.getValue("previousItems");
if (previousItems == null) {
previousItems = new ShoppingCart(...);
}
String itemID = request.getParameter("itemID");
previousItems.addEntry(Catalog.getEntry(itemID));
session.putValue("previousItems", previousItems);
10.3 執行個體:顯示會話資訊
下面這個例子產生一個Web頁面,並在該頁面中顯示有關當前會話的資訊。
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;
import java.util.*;
public class ShowSession extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Searching the Web";
String heading;
Integer accessCount = new Integer(0);;
if (session.isNew()) {
heading = "Welcome, Newcomer";
} else {
heading = "Welcome Back";
Integer oldAccessCount =
// 在Servlet API 2.2中使用getAttribute而不是getValue
(Integer)session.getValue("accessCount");
if (oldAccessCount != null) {
accessCount =
new Integer(oldAccessCount.intValue() + 1);
}
}
// 在Servlet API 2.2中使用putAttribute
session.putValue("accessCount", accessCount);
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + heading + "</H1>\n" +
"<H2>Information on Your Session:</H2>\n" +
"<TABLE BORDER=1 ALIGN=CENTER>\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
" <TH>Info Type<TH>Value\n" +
"<TR>\n" +
" <TD>ID\n" +
" <TD>" + session.getId() + "\n" +
"<TR>\n" +
" <TD>Creation Time\n" +
" <TD>" + new Date(session.getCreationTime()) + "\n" +
"<TR>\n" +
" <TD>Time of Last Access\n" +
" <TD>" + new Date(session.getLastAccessedTime()) + "\n" +
"<TR>\n" +
" <TD>Number of Previous Accesses\n" +
" <TD>" + accessCount + "\n" +
"</TABLE>\n" +
"</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}