HTTP是无状态的协议:客户端每次读取Web页面,都打开到Web服务器单独的连接,并且服务器也不自动维护客户的上下文信息。(此处翻译的不太好,猜测原版中肯定是Context Information)上下文的丢失会引起很多困难,典型的就是在线商店。对于这个问题有三种解决办法:Cookie,URL重写,隐藏的表彰域:
Cookie:使用Cookie存储购物会话ID,在后续的连接中,取出当前的会话ID,并使用这个ID从服务器上查找表中提取出会话的相关信息;
URL重写:客户端程序在每个URL的尾部添加一些额外的数据,这些数据标识当前的会话,服务器将这个标识符与它存储的用户相关数据关联起来;
隐藏的表单域:在提交表单时,要将指定的名称和值自动包括在GET或POST数据中,如:<input type="hidden" name="session" value="1234" />
Servlet中的会话跟踪
Servlet提供了一种出色的会话跟踪解决方案:HttpSessionAPI。这个高层接口构筑在cookie或URL重写之上。所有的服务器都需要支持使用cookie的会话跟踪,大多数服务器提供一项设置,可以全局的切换URL重写。不论采取哪种方式,Servlet开发人员都不需要为许多具体的细节实现操心,也不需要显示地操作Cookie或添加到URL上的信息,它们自动获得一个区域,可以方便的存储与每个会话相关联的任意对象。
在Servlet中会话跟踪比较简单,有四个步骤:
- 访问与当前请求相当的会话对象:调用request.getSession方法获取session对象,该对象是一个简单的散列表,用于存放用户的相关数据。(散列表——若结构中存在关键字和K相等的记录,则必定在f(K)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f为散列函数(Hash function),按这个思想建立的表为散列表。——总感觉是离散数学里的东东,忘没了:P);
- 查找与会话相关联的信息:调用HttpSession对象的getAttribute,将返回值转换成一个恰当的类型,并检查结果是否为null;
- 存储会话中的信息:使用setAttribute设置要存储的值以及相应的键;
- 废弃会话数据:调用removeAttribute废弃指定的值。调用invalidate废除整个会话。调用logout使得客户退出Web服务器,并作废与该用户相关的所有会话。
URL编码
如果使用URL重写进行会话跟踪,大部分或全部页面必须动态生成,站点上的任何静态HTML页面都不能含有指向自身站点动态页面的链接。有两种情况可能会引用自身站点的URL:
1.Servlet生成的Web页面中含有嵌入的URL。这种情况下,应该将这些URL传递给HttpServletResponse的encodeURL方法。如:
2.在sendRedirect调用中(即放入Location响应报头)。这种情况下由于要根据不同的规则确定是否需要附加会话信息,因而不能使用encodeURL。但可以使用HttpServletResponse提供的encodeRedirectURL方法来处理,如:
一个小例子
显示客户端访问的计数,该 Servlet使用request.getSession获取现存的会话,或在没有会话的情况下创建新会话,之后,查找类开为Integer的accessCount属性。找不到则用0作为之前的访问计数。然后对这个值进行递增,并用setAttribute关联起来。
ShowSession.java: