`
Luob.
  • 浏览: 1573912 次
  • 来自: 上海
社区版块
存档分类
最新评论

Struts2 阻止表单重复 和 等待页面(四十四)

 
阅读更多
表单重复提交现象

导致表单重复提交的现象可以分为一下两种情况:
1.多次单击提交按钮
   对响应来不及。用户没有看到提交后的结果,导致用户频繁多次的点击提交按钮。
2.执行刷新操作
   对响应及时,也有可能出现表单重复提交现象。服务器段的程序在处理哟过户提交哦啊的信息后,调用RewuestDispatcher.forward()方法,将哟过户请求导向成功的页面,用户看到成功信息后,对成功页面执行花心操作,这是浏览器将再次提交用户先前输入的信息。


同步令牌的基本原理
在服务器端避免表单重复提交,通常采用同步令牌的方式来实现。同步令牌的基表原理如下:
1:服务器端在处理客户端请求时,创建一个Session对象和一个令牌(例如 token1)。然后 将token1作为影藏域随处理结果一起发送到客户端,同事将token1保存到session中
2.服务器端在处理到达的请求之前,将请求中的token1与保存在当前用户session中的值进行比较,肩擦这两个值是否匹配。
3.如果相等。表示用户是第一次提交表单,则清楚session中的token1,然后执行数据处理操作,同时产生一个新的令牌值(例如 token2) 然后保存到session中,当用户重新访问提交数据页面时,将新产生的token2做为表单影藏域的值。
4.如果用户重复提交,客户端传递过来的令牌值是token1,而服务器端的令牌为token2,这两个令牌的值不等,于是不在对用户的请求进行提交,从而有效防止表单重复提交发生。


Struts2 的实现方式
 Struts2框架提了token标签,使用该标签,需要指定一个令牌的名字,例如:<s:token name="user.token"/> token标签将创建一个新的令牌值,并根据指定的令牌名称将令牌值保存到session中。
token标签最终将会在表单中生成两个影藏字段。请求包含token标签的表单文件后,在运行页面的源文件中可以看到如下形式的代码:
  <input type="hidden" name="struts.token.name" value="user.token"/>
  <input type="hidden" name="user.token" value="5J6RS8NCMZSQBH7NZDX8XRG435O2XAP"/>
第一个隐藏字段的名字是struts.token.name 是固定的,第二个影藏字段的名称是user.token 它的value的值是令牌值。
服务器首先根据stuts.token.name 找到保存令牌的请求参数名,user.token 然后获取user.token请求参数得到令牌值。

token标签必须与token,tokenSession或者 execAndWait等拦截器配合使用,这3个拦截器都能对token标签进行处理
 上述3个拦截器的实现类分别为了TokenInterceprot,TokenSessionStoreInterceptor 和ExecuteAndWaitInterceptor,他们已经在Struts-default.xml文件中定义,但是没有包含在defaultStack拦截器栈中。


----使用 token 拦截器---


import com.opensymphony.xwork2.ActionSupport;

public class LoginAction extends ActionSupport{

	private String userName;
	private String userPassword;
	
	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		return  SUCCESS;
	}
// get set
}

struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">

<struts> 
	<!-- 如果 注解的action配置改变时候不需要重新启动tomcate -->
	<constant name="struts.devMode" value="true"/>  
    <constant name="struts.convention.classes.reload" value="true" /> 
	
	<package name="default" extends="struts-default">
		<action name="login" class="com.sh.action.LoginAction">
			<interceptor-ref name="defaultStack"/>
			<interceptor-ref name="token"/>
			<result name="invalid.token">/login.jsp</result>
			<result name="input">/login.jsp</result>
			<result name="success">/index.jsp</result>
		</action>
		
	</package>
    
</struts>


login.jsp
 <body>
   <STRONG>用户登录</STRONG>
   <s:form action="login">
   		<s:token/>
   		<s:actionerror/>
   		<s:textfield name="userName" label="姓名"/>
   		<s:textfield name="userPassword" label="密码"/>
   		<s:submit value="登录"/>
   </s:form>
  </body>

index.jsp
 <body>
    	<h4>用户登录成功</h4>
    	登录名称:${userName}
  </body>


----使用 tokenSession 拦截器---
   tokenSession拦截器扩展了token拦截器,但是tokenSession拦截器不会返回一个特殊的结果,也不会添加一个动作错误,只是阻断后面的提交,这样做的结果就是用户将看到你同样的响应,就好像只有一次提交。
   如果使用浏览器的后退功能,退回到登录页面,即使使用其他名称登录,提交表单后仍然显示原来扽牢固的名称。也就是说tokenSession拦截器只承认第一次提交。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">

<struts> 
	<!-- 如果 注解的action配置改变时候不需要重新启动tomcate -->
	<constant name="struts.devMode" value="true"/>  
    <constant name="struts.convention.classes.reload" value="true" /> 
	
	<package name="default" extends="struts-default">
		<action name="login" class="com.sh.action.LoginAction">
			<interceptor-ref name="defaultStack"/>
			<interceptor-ref name="tokenSession"/>
			<!-- 如果使用 token 下面的就有这样的一个返回值
			<result name="invalid.token">/login.jsp</result> -->
			<result name="input">/login.jsp</result>
			<result name="success">/index.jsp</result>
		</action>
		
	</package>
    
</struts>


---使用 ExecAndWait拦截器
    在 web应用中,往往会遇到需要执行很长时间的页面,例如上传一个很大的文件,服务器接受请求后,需要处理批量的数据。这种情况下,如果没有任何设置,那么在服务器处理的这个过程中。浏览器将呈现一片空白,这时用户不知道程序 有没有执行,很可能刷新该页面,甚至执行关闭页面的操作,因此,在等待处理的过程中,设置一个友好的信息提示页面是很有必要的。

使用execAndWait拦截器实现自动显示等待页面的具体过程如下:
(1) 当表单提交请求到来时。execAndWait拦截器将创建一个新的线程来执行Action,然后返回一个等待页面给用户,然用户只懂啊请求正在处理中。
(2) 等待页面将包含自动刷新功能,每隔几秒就通知浏览器,向初始请求的URL再次发送请求
(3) execAndWait拦截器再次截获请求,判断Action是否执行完毕,如果仍未执行完毕,则继续向用户返回等待页面;如果已经执行完毕,则向用户返回相应的执行成功页面。

execAndWait拦截器包含一下3个参数:

threadPriority:可选参数,用来指定线程的优先级。默认值为Thread.NORM_PRIORITY.

delay:可选参数,用来显示等待页面前初始的等待延迟时间,以毫秒为单位,默认情况下,没有等待延迟。

delaySleepInterval:可选参数,只能和delay参数一起使用,用来指定检查后进程是否执行完毕的时间间隔,以毫秒为单位,默认值为100毫秒。

初始等待延迟:就是可以让服务器在显示等待页面之前延迟一段时间,单位是毫秒。
在初始等待延迟时间里,execAndWait拦截器每隔100毫秒检查后台Action的执行情况,如果 Action对请求的处理,并不需要很长的时间,则等待页面不会被显示,如果Action需要较长的执行时间,则将等待页面返回给用户。

action.java  login.jsp index.jsp 和上面的一样
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">

<struts> 
	<!-- 如果 注解的action配置改变时候不需要重新启动tomcate -->
	<constant name="struts.devMode" value="true"/>  
    <constant name="struts.convention.classes.reload" value="true" /> 
	
	<package name="default" extends="struts-default">
		<action name="login" class="com.sh.action.LoginAction">
			<interceptor-ref name="defaultStack"/>
			
			<interceptor-ref name="execAndWait">
				<!-- 设置服务器处理请求的时间 ,如果超过这个时间 将显示等待页面 wait.jsp -->
				<param name="delay">1000</param>
			</interceptor-ref>
			<result name="wait">/wait.jsp</result>
			
			<!-- 使用 tokenSession 拦截器
			<interceptor-ref name="tokenSession"/> -->
			
			<!-- 如果使用 token 下面的就有这样的一个返回值
			<interceptor-ref name="token"/>
			<result name="invalid.token">/login.jsp</result> -->
			
			<result name="input">/login.jsp</result>
			<result name="success">/index.jsp</result>
		</action>
		
	</package>
    
</struts>


wait.jsp
<head>
    <base href="<%=basePath%>">
    
    <title>避免表单重复提交</title>

    <!--设置每5s刷新 这个页面-->
    <meta http-equiv="refresh" content="5,url=<s:url includeParams='all'/>">

  </head>
  
  <body>
   	<h4>正在登录系统<br/><br/>
   	请您稍候...</h4>
  </body>
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics