大多数做过基于Web的Java编程的人都做过翻页提交这种比较基本的工作这些网络编程中不可缺少的步骤通常都很容易实现但不知你有没有过这样的经历在一些特殊情况下翻页出现了混乱明明下一页应该是第页却翻到第页明明只提交了一次却发现在购物车里提交了两次结果千万别以为是自己眼花了或者是遭病毒袭击了这些就是我们编程中容易碰到的陷阱下面我们就这两个问题分别进行讨论
翻页中的陷阱
翻页有两种常用的实现方式基于session的翻页和基于hidden的翻页
将页号信息保存在session中
这是最常使用的方法这种方法的初衷是利用session在页面之间传递信息即将页号信息保存在session变量中这样上下翻页时只需从session中取出当前页号并进行相应的处理当有不同的查询页面指向同一个结果页面时可在不同的查询页面内将session内的页号值重置可较好地实现复用
很多人习惯采用如下实现方法
假设有两个查询页面timejsp和placejsp分别以时间和地点为查询条件结果指向同一个显示页面resultjsp在timejsp和placejsp中均有这样的语句
<%page.setPageNum(1);%>
在resultjsp中有这样的语句
下一页<%page.nextPage();%>
上一页<%page.previousPage();%>
在类page中有这样的代码
private int pageNum;
public void setPageNum(int i){
pageNum = i;
}
public void previousPage(){
pageNum ;
}
public void nextPage(){
pageNum ++;
}
现在来看一看陷阱是怎么产生的
当以时间为查询条件时下翻到第页将此窗口命名为窗口此时重新打开一个窗口(Ctrl+N方式)以地点为查询条件下翻到第页将此窗口命名为窗口到目前为止一切正常但请注意当再回到窗口并向下翻时问题便出现了——原本应该是第页现在显示的却是第页
为什么呢?原因很简单两个窗口共享一个session值当窗口切换回窗口时窗口引用的pageNum值已被窗口改变了
由此可以发现session在不同页面之间共享信息固然很有用但必须小心使用否则会制造出一些隐藏很深的错误
通过URL再次传递页号信息
先来看一下具体的实现方法例子仍基于上述情况首先在timejsp和placejsp中删除对JavaBean的调用分别在两个文件的提交url后追加页号信息例如在timejsp中加入代码resultjsp?date=&&pageNum=在placejsp中加入代码resultjsp?place=bj&&pageNum=
其次在resultjsp中做如下设置将url中所有变量以hidden的形式重新写入form中下面为其代码实例
这种方法看似愚笨让人有多此一举的感觉但事实上它却是出现漏洞最少的一种方法这样以来每个结果页面都有自己独立的页号信息不存在干扰情况从而避开了陷阱
重复提交中的陷阱
当我们进行网上购物时在选择了满意的商品后就要进行提交操作如果网速较慢或有其它因素影响就会迟迟不出结果于是我们常常回退或停止然后再次提交这次很快有了响应但奇怪的是明明只提交一次购物车中的商品却是双份的产生原因是这样的当因为迟迟不响应而回退时在服务器端有两种可能一种是提交已经得到了处理数据库中已有了相应记录但结果页面没有显示出来另一种是提交还未得到处理这种没有任何问题
针对这个现象解决办法是引入同步机制(SynId)指导思想就是为每一个页面编号并在客户端和服务器端各产生一个副本每次通过比较两端的编号是否一致达到同步的目的首先由服务器产生这个编号发送到客户端这样这个编号在服务器和客户端各有一个副本当客户提交页面时服务器首先比较两个编号是否一致如果一致则处理提交并产生一个新的编号返回给客户端此时如果客户回退并再次提交客户端是旧编号服务器端是新编号显然不一致因此服务器将判定这是一次重复提交不予受理
实现过程如下(如图所示)
图 同步机制实现过程
当用户首次购物时发出购物请求完成步骤——在服务器端产生一个同步环保存在session中客户在购物页面完成步骤——将同步环保存在客户端的hidden中客户选购完毕提交请求步骤启动——服务器比较session中的同步环和客户提交的是否一致如果一致服务器处理请求完成步骤然后进行步骤——产生一个新的同步环并将结果页面返回给客户如果此时客户尚未看到结果页面进行了回退或停止重新提交操作用户提交页面的同步环显然不同于服务器session中的同步环于是服务器会判定这是一个重复提交的页面不予受理
对于同步环的产生保存比较最好生成一个新同步助手类SynHelper完成相应的操作调用它的Jsp文件只需以JavaBean的形式引用即可下面描述作为类SynHelper所需的基本方法
private String generateSynId(){
return LongtoString(SystemcurrentTimeMillis());
}
protected void saveSynId(HttpServletRequest request){
HttpSession session = requestgetSession();
sessionsetAttribute(SYN_IDgenerateSynId());
}
protected boolean compareSynId(HttpServletRequest request){
try{
HttpSession session = requestgetSession();
String serverSynId = sessiongetAttribute(SYN_ID);
String clientSynId = requestgetParameter(CLIENT_SYN_ID);
Return (serverSynIdequals(clientSynId));
}catch(Exception e){
return false;
}
}
对于客户端jsp在收到服务器的同步环后必须将其保存在hidden中具体实现如下
value=<%=session.getAttribute(“SYN_ID”)%>>