Struts2中防止表单重复提交的两种方式

前言

防止表单重复提交,这是个很重要的知识点,而且经常会用到。当用户提交了一个表单,此时,地址栏显示的是处理这个表单的Action的地址,若此时刷新,则会重新发送一次表单数据,即又进行了一次提交,若这个Action是用来处理用户注册的,那么重复提交会再一次向数据库中插入之前已经插入的数据,这显然不是我们想要的。有两种方法,可以防止表单重复提交,一种是用Action的重定向,一种是用Session Token(Session令牌)。

第一种方法,Action处理完用户提交的数据后,重定向到另一个Action或是一个页面,地址栏会发生变化,使用户提交后,所停留的位置,不是当前处理数据的Action,这样用户再刷新时,就不会再次执行这个Action了,就会避免表单重复提交的问题了。

第二种方法,是一种很经典的处理这个问题的机制。这种方法是在用户要提交的表单中,加入一个<s:token>标签,这样,当浏览器第一次访问这个带有<s:token>标签的页面时,在服务器中,解析<s:token>标签的类,会生成一个随机的字符串(这个字符串,查看网页的源代码可以看到),并且发送给客户端的浏览器,同时,在服务器中,会把这个随机字符串保存到用户的session对象中。当第一次提交表单时,在服务器中,会比较客户端和服务器中分别保存的这个随机字符串,因为是第一次提交,所以这两个字符串相等,然后进行正常的业务处理。第一次提交后,在服务器中的session中保存的这个随机字符串,会改变为其他的随机值,注意,这是很重要的一步!此时,地址栏停留在处理用户提交数据的Action中,客户端中保存的随机字符串没有改变,若是刷新页面,即重复提交,服务器再进行两个字符串的比较,会不相等,就会跳转到name为invalid.token的结果页面中,这样就会防止表单重复提交了。
我们根据具体的案例来实现上面两个不同防止表单提交的方法。

案例

本案例主要的设计如下图所示:
这里写图片描述

需求描述:三个功能:register.jsp -注册页面,用户注册保存到数据库中。保存成功跳转到用户信息列表list.jsp.然后在list.jsp页面可以进行修改操作。其中会在register.jsp表单中添加token标签,让其生成随机值,浏览器跟服务器各一份。

数据库表结构

CREATE TABLE employee (
   id INT PRIMARY KEY AUTO_INCREMENT,
   empName VARCHAR(20),
   workDate DATE      -- 入职时间
)

代码如下

EmployeeAction.java

public class EmployeeAction extends ActionSupport implements ModelDriven<Employee> {

    private Employee employee = new Employee();
    private EmployeeService service = new EmployeeService();

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    @Override
    public Employee getModel() {
        return employee;
    }
    //注册
    public String register(){
        try {
            service.save(employee);
            return list();
        } catch (Exception e) {
            e.printStackTrace();
            return INPUT ;

        }

    }
    /**
     * 列表显示
     */
    public String list(){
        try {
            List<Employee> employeeList =service.getAll();
            for (Employee employee : employeeList) {
                System.out.println(employee.getWorkDate());
            }
            //保存到域对象
              ActionContext.getContext().getContextMap().put("employeeList", employeeList);
            return "list";
        } catch (Exception e) {
            e.printStackTrace();

        }
        return ERROR;
    }
    /**
     * 跳转到修改视图
     */
    public String viewUpdate(){
        try {

            Employee emp = service.findById(employee.getId());
            //保存到值栈中
            ValueStack stack = ActionContext.getContext().getValueStack();
            Employee pop = (Employee)stack.pop();
            stack.push(emp);
            return "ViewUpdate";
        } catch (Exception e) {
            e.printStackTrace();

        }
        return ERROR;
    }
    /**
     * 修改
     */
    public String update(){
        try {
            service.update(employee);
            return list();
        } catch (Exception e) {
            e.printStackTrace();

        }
        return ERROR;
    }

}

Register.jsp

<form action="${pageContext.request.contextPath }/employee_register.action" method="post"> 
   <s:token></s:token>
      员工名:<input type="text" name="empName">
      <s:fielderror fieldName="employee.empName"></s:fielderror><br/>
      入职时间:<input type="text" name="workDate">
      <s:fielderror fieldName="employee.workDate"></s:fielderror><br/>
      <input type="submit" value="注册">
   </form>

List.jsp

<table align="center" border="1px" cellpadding="0px" cellspacing="0px">

    <tr>
     <th>序号</th><th>编号</th><th>员工名</th><th>入职时间</th><th>操作</th>
    </tr>
    <s:if test="%{#request.employeeList!=null}">
    <s:iterator var="emp" value="%{#request.employeeList}" status="vs">
     <tr>
      <td><s:property value="#vs.count"/></td>
      <td><s:property value="#emp.id"/></td>
      <td><s:property value="#emp.empName"/></td>
      <td><s:date name="%{#emp.workDate}"/></td>
      <td><s:a href="employee_viewUpdate?id=%{#emp.id}">修改</s:a></td>    
     </tr>
     </s:iterator>
    </s:if>
    <s:else>
     <tr>
      <td colspan="5">没有员工信息,请先注册!</td>
     </tr>
    </s:else>

  </table>

Update.jsp

 <s:form action="/employee_update">
   <s:hidden name="id" ></s:hidden>
     <s:textfield name="empName"/><br/>
    <s:date name="workDate"/>
     <s:submit value="修改"></s:submit>
   </s:form>

Struts.jsp

<struts>
<!-- 修改主题 (当前项目所有的标签都用此主题)-->
        <constant name="struts.ui.theme" value="simple"></constant>
    <package name="employee"  extends="struts-default">

        <global-results>
            <result name="error">/error.jsp</result>
        </global-results>
        <action name="employee_*" class="edu.action.EmployeeAction"
            method="{1}">

            <!-- 防止表单重复提交,第二步: 配置" 防止表单重复提交拦截器" -->
            <interceptor-ref name="defaultStack"></interceptor-ref>
            <interceptor-ref name="token">
                <!-- 指定拦截哪些方法需要防止表单重复提交(register) -->
                <param name="includeMethods">register</param>
            </interceptor-ref>

            <!-- 防止表单重复提交,第三步: 如果用户重复提交了跳转到指定的错误页面  -->
            <result name="invalid.token" type="redirectAction">employee_list</result>

            <!-- 如果不用重定向默认是转发 地址不会变type="redirectAction" -->

            <result name="input">/register.jsp</result>
            <result name="ViewUpdate">/WEB-INF/ViewUpdate.jsp</result>
            <result name="list">/WEB-INF/list.jsp</result>
        </action>
    </package>

</struts>

可以看到在访问register页面时的源代码如下:

<form action="/d909_Demo/employee_register.action" method="post"> 
   <input type="hidden" name="struts.token.name" value="token" />
<input type="hidden" name="token" value="6FY7JZ0ME5YTZ809L2LR7EKWPHPXM77G" />
      员工名:<input type="text" name="empName">
      <br/>
      入职时间:<input type="text" name="workDate">
      <br/>
      <input type="submit" value="注册">
   </form>

服务器自动生成随机值,用于第二次访问时的比较。这样就可以防止表单重复提交了。
其他像service层、dao层代码这里就不再赘述了。

第二种防止表单重复提交的方式:

只需要把Action中register页面返回值在result中配置成重定向即可。

修改代码如下:
Action中的register方法

public String register(){
        try {
            service.save(employee);
            return "addSuccess";
        } catch (Exception e) {
            e.printStackTrace();
            return INPUT ;
        }   
    }

struts.xml增加结果视图如下:

<result name="addSuccess" type="redirectAction">employee_list</result>

这样每次注册完之后,地址栏发生变化,避免了表单重复提交。
地址栏显示:http://localhost:8080/d909_Demo/employee_list.action

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值