一个小例子,模拟银行转账,在转账的页面,点击转账,就可以向对方账户进行转账
但是转账成功之后,如果刷新页面,就会弹出一条提示,如果点击继续,就会重复提交,再转账一次,不断刷新,也就会不断转账,那么如何解决表单重复提交的问题呢?
可以使用一个标志,在服务器端存储一个标志(令牌),然后在客户端也存储一个,通过匹配两个令牌,来判断是否可以转账
在第一次请求转账页面的时候会创建令牌,然后在转账后(无论成功与否)删除服务器端的令牌,这样下次刷新后再请求转账,就会出现令牌不匹配的情况,也就无法再次转账了,这样就解决了表单重复提交的问题
代码演示
使用JavaWeb工程,JSP+Servlet技术
在工具类中创建一个令牌的工具类
两个方法:
- 创建令牌:将令牌存储到session中,创建时也就是存储在服务器端(实际上令牌就是一个随机数)
- 删除令牌:删除session中存储的令牌
package com.robot.utils;
import javax.servlet.http.HttpServletRequest;
/**
* 令牌工具类。
*
* @author 张宝旭
*/
public class TokenUtils {
/**
* 创建令牌
*/
public static void makeToken(HttpServletRequest request, String tokenName) {
String token = String.valueOf(System.currentTimeMillis() + Math.random() * 9999);
request.getSession().setAttribute(tokenName, token);
}
/**
* 删除令牌
*/
public static void removeToken(HttpServletRequest request, String tokenName) {
request.getSession().removeAttribute(tokenName);
}
}
在转账的jsp页面中先获取服务器端的令牌
<%
TokenUtils.makeToken(request, "serverToken");
String serverToken = (String) session.getAttribute("serverToken");
%>
然后在表单中跟随转账账号和金额一同提交给Servlet,使用hidden="hidden"表示隐藏此输入框,就是为了提交到Servlet,所以不用显示
<input type="text" name="clientToken" value="${serverToken}" hidden="hidden">
在处理转账的Servlet中创建一个方法,用以判断令牌的状态,如果发现表单重复提交,就返回true
public boolean isRepeat(HttpServletRequest request) {
// 获取服务器端令牌
Object serverToken = request.getSession().getAttribute("serverToken");
// 获取客户端令牌
String clientToken = request.getParameter("clientToken");
// 判断
if (serverToken == null || clientToken == null) {
return true;
}
if (!serverToken.equals(clientToken)) {
return true;
}
return false;
}
再在doGet或doPost方法中添加判断之后的处理,如果发生表单重复提交,就转发到另一个提示的页面,显示“页面已失效”,如果没有发生表单重复提交,就将令牌删除掉,防止表单重复提交,然后就正常执行后面的处理转账的语句
if (isRepeat(request)) {
request.setAttribute("msg", "页面已失效");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
TokenUtils.removeToken(request, "serverToken");
演示
在转账成功后,刷新页面,会提示是否重复提交表单,然后点击继续,则跳转到指定页面
说明已经成功解决了表单重复提交的问题
如果想要再次转账,那么点击返回,也是不行的,因为令牌是在请求jsp页面的时候创建的,所以需要在返回到转账页面之后,再刷新一下,也就是重新请求一次,就可以再正常转账了。