您的位置:首页 > 编程语言 > Java开发

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

2018-01-09 21:34 465 查看

前言

防止表单重复提交,这是个很重要的知识点,而且经常会用到。当用户提交了一个表单,此时,地址栏显示的是处理这个表单的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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息