-
什么是表单重复提交
- 表单的重复提交:
- 若刷新表单页面, 再提交表单不算重复提交.
- 在不刷新表单页面的前提下:
- 多次点击提交按钮
- 已经提交成功, 按 "回退" 之后, 再点击 "提交按钮".
- 在控制器响应页面的形式为转发情况下,若已经提交成功, 然后点击 "刷新(F5)“
- 重复提交的缺点:
- 加重了服务器的负担
- 可能导致错误操作.
-
token(令牌)
- 用户在访问页面时,我们要生成一个随机的token值
- 将该值放入到session域中,同时放在表单隐藏域中
- 用户提交请求时,隐藏域中token将会一起提交
- 服务器在处理请求时先要检查请求参数中的token和session中的token是否一致
- 如果一致,则处理请求
- 否则,返回错误页面
- 注意,token使用过一次以后必须要从session域中移除
Struts2 解决表单重复提交
-
标记管理
- Struts 提供的 token 标签可以用来生成一个独一无二的标记. 这个标签必须嵌套在 form 标签的内部使用, 它将在表单里插入一个隐藏字段并把标记值(隐藏域的字段的值)保存在HttpSession 对象里.
- Token 标签必须与 Token 或 TokenSession 拦截器配合使用, 这两个拦截器都能对标记进行处理.
- Token 拦截器在遇到重复提交情况时, 会返回 invalid.token 结果并加上一个 Action 错误. 这种错误默认的消息是: The form has already been processed or no token was supplied, please try again.
- TokenSession 拦截器采取的做法只是阻断后续的提交, 用户将看到同样的响应,但实际上并没有重复提交
-
Struts2 解决表单的重复提交问题:
-
在 s:form 中添加 s:token 子标签
- 生成一个隐藏域
- 在 session 添加一个属性值
- 隐藏域的值和 session 的属性值是一致的.
-
使用 Token 或 TokenSession 拦截器.
- 这两个拦截器均不在默认的拦截器栈中, 所以需要手工配置一下
- 若使用 Token 拦截器, 则需要配置一个 token.valid 的 result
- 若使用 TokenSession 拦截器, 则不需要配置任何其它的 result
-
Token VS TokenSession
- 都是解决表单重复提交问题的
- 使用 token 拦截器会转到 token.valid 这个 result
- 使用 tokenSession 拦截器则还会响应那个目标页面, 但不会执行 tokenSession 的后续拦截器. 就像什么都没发生过一样!
-
可以使用
s:actionerror
标签来显示重复提交的错误消息.- 该错误消息可以在国际化资源文件中覆盖. 该消息可以在 struts-messages.properties 文件中找到
struts.messages.invalid.token=The form has already been processed or no token was supplied, please try again.
- 该错误消息可以在国际化资源文件中覆盖. 该消息可以在 struts-messages.properties 文件中找到
-
<s:form action="testForm" >
<!-- 增加token 标签,隐藏属性 -->
<s:token></s:token>
<s:textfield name="username" label="用户名"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>
<!-- token 拦截器 -->
<action name="testForm" class="org.pan.action.TestFormAction" method="testForm">
<!-- 使用token 拦截器,进行表单重复提交校验 -->
<interceptor-ref name="token"></interceptor-ref>
<!-- 不要忘记默认的拦截器栈-->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 如果是使用的是 token 拦截器,校验失败后会转到 invalid.token -->
<result name="invalid.token">/WEB-INF/views/token-error.jsp</result>
<result name="success">/WEB-INF/views/success.jsp</result>
</action>
<action name="testForm" class="org.pan.action.TestFormAction" method="testForm">
<!-- 使用tokenSession 拦截器,进行表单重复提交校验 -->
<interceptor-ref name="tokenSession"></interceptor-ref>
<!-- 不要忘记默认的拦截器栈-->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 如果是使用的是 tokenSession 拦截器,校验后会回到返回的 action方法 result。就像什么都没发生过 -->
<result name="success">/WEB-INF/views/success.jsp</result>
</action>