下面我们首先来说一下表单的重复提交问题,我们知道在真实的网络环境中可能受网速带宽的原因会造成页面中表单在提交的过程中出现网络的延迟等问题,从而造成多次提交的问题!下面我们就具体来分析一下造成表单提交的一些常见问题。
下面我们就来列举一下重复提交的情况:
① 、当表单提交数据到一个 Servlet 中,然后 Servlet再通过请求转发到成功页面,但是此时的地址栏中的地址是到 Servlet映射中的地址,并没有跳转到成功页面相关的JSP页面中,此时刷新页面会造成再一次提交表单。
② 、当在表单页面中点击提交按钮时,表单页面没有立即跳转到Servlet来进行处理(网络延迟),而此时用户就点击多次提交按钮,也会造成重复提交表单!
③ 、当用户进行表单数据提交成功后,又通过浏览器上的返回按钮返回到提交表单页面,(在不进行刷新到条件下)再次进行点击提交也会造成重复提交表单的情况!这是因为在不进行刷新的情况下页面的表单数据是浏览器第一次提交的缓存,故而进行提交的还是第一次提交的数据。而进行刷新后则是一个新的request请求!
对此我们如何进行防止表单的重复提交呢?下面我们来进行说一下:
① . 我们提供一个隐藏域: <input type="hidden" name="token" value="vincent"/>.来进行作为标记。这时我们发现当我们将数据提交到Servlet中时,没有方法清除固定的请求参数(即:标记).所以该方案是行不通的!
② . 把标记放在 request 中.这时虽然我们可以通过作用域的removeAttribute(str);方法将标记进行删除 但是当我们请求表单页面时虽然将标记保存到request中, 而我们点击提交按钮时,将会向Servlet重新提交一个新的request,而此时第一个request已经失去了它的作用域(我们知道request只能在一次请求之间有效!)被销毁掉,故而无法在目标Servlet中获取到第一request设置的属性(标记),所以该方案也行不通!
③. 把标记放在 session 中. 可以!同时我们也可以使用隐藏域来帮助我们将标记变成一个随机值。
> 在原表单页面, 生成一个随机值 token
String tokenValue = new Data().getTime() + “”;
(使用时间来作为随机值,还不够随机,不过暂时对于我们学习而言还可以,切记在工作中使用时间来作为随机值,因为在大量的用户访问我们的产品时,时间就不能作为随机数了)
> 在原表单页面, 把 token 值放入 session 属性中
session.setAttribute(“token”,tokenValue);
> 在原表单页面, 把 token 值放入到 隐藏域 中.
<input type=”hidden” name=”token”value=<%=tokenValue%> />
> 在目标的 Servlet 中: 获取 session 和 隐藏域 中的 token 值
Object token = request.getSession.getAttribute(“token”);
String tokenValue = request.getParameter(“token”);
> 比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 token 属性清除
> 若不一致, 则直接响应提示页面: "重复提交"
if( token != null && token.equals(tokenValue)){
session.removeAttribute(“token”);
}else{
response.sendRedirect("repeated.jsp");//响应提示页面: "重复提交"
}
response.sendRedirect("success.jsp");
以上就可以解决表单重复提交的问题了!此外当我们在框架中如Struts1、Struts2、SpringMVC中也可以看到它们也提供了相应的解决方法!
下面我们来说一下,关于验证码的问题:
对于为什么要使用验证码的原因相信大家都知道在此就不在赘述了!其实对于验证码来说,和防止表单重复提交一样的原理一样。
> 在原表单页面, 生成一个验证码的图片, 生成图片的同时, 需要把该图片中的字符串放入到 session 中.
> 在原表单页面, 定义一个文本域, 用于输入验证码.
> 在目标的 Servlet 中: 获取 session 和 表单域 中的 验证码的 值
> 比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 验证码 属性清除
> 若不一致, 则直接通过重定向的方式返回原表单页面, 并提示用户 "验证码错误"