struts2 防止重复提交 与 进入等待画面

演示重复提交的错误:

相关文件:


struts.xml

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

<struts>
	<package name="strutsqs" extends="struts-default">
	
		<action name="Login" class="com.gq.LoginAction">
			<result name="error">/error.jsp</result>
			<result name="success">/success.jsp</result>
		</action>
		
	</package>
</struts>
login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'login.jsp' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
    <form action="Login.action" method="post">
    	<table width="207" border="1" height="82">
<tbody><tr>
<td> UserName:</td>
<td> <input type="text" name="username"></td></tr>
<tr>
<td> Password:</td>
<td> <input type="password" name="password"></td></tr>
<tr>
<td> <input type="submit" value="Login"></td>
<td> <input type="reset" value="Reset" name="reset"></td></tr>
</tbody></table>
    </form>
  </body>
</html>
LoginAction.java

public class LoginAction {
	private String username;
	private String password;
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	
	@SuppressWarnings("unchecked")
	public String execute() throws Exception{
		
		// Just for test of token.
		System.out.println( "name:" + getUsername() + "\tpassword:" + getPassword());
		consumeTimeForToken();
		
		if( getUsername().equals("gqltt") && getPassword().equals("123")){
			addToSessionScope("user", getUsername());
			return "success";
		}
		
		return "error";
	}
	
	@SuppressWarnings("unchecked")
	void addToSessionScope( String key, String value ){
		ActionContext.getContext().getSession().put(key, value);
	}
	
	//消耗时间,提供重复提交的机会
	void consumeTimeForToken(){
		int result = 0;
		for( int i = 0 ; i<30000000 ; i++ ){
			result += (int)i/551.22;
			result /= 3.1458;
		}
		System.out.println("result=" + result);
	}
	
	// Just used for unit test!
	String findCompanyByName( ){
		Map<String, String> records = new HashMap<String, String>();
		records.put("gqltt", "SNJP");
		records.put("Liyanhong", "baidu");
		records.put("Bill", "Microsoft");
		
		return records.get( getUsername() );
	}
}
提交页面:

输出结果:

解决办法:

1、在提交页面的 form 中添加 <s:token/>


2、在Struts2 的配置文件中启用 TokenInterceptor拦截器或 TokenSessionStoreInterceptor拦截器

注意:

1、必须配置默认的拦截器(basicStack),否则取不到数据,抛出 NullPointerException

2、必须指定重复提交后的错误页面(invalid.token)

优点:可以防止客户重复提交,大大地降低了服务器的负荷。

缺点:对用户来说,可能会很不方便,一不小心点击了提交按钮,进入到了invalid.token页面,就再也回不去了,上述的操作就再也看不见了。(的确很恶心,即使倒退回登录页面,再次正常登录,还是会进入 invalid.token 页面!除非关掉网页再打开。)

参考:http://chengyue2007.iteye.com/category/73492?show_full=true

等待画面:

struts.xml 添加配置:

<action name="LongLived" class="com.gq.LongLivedAction">
  <interceptor-ref name="completeStack"/> 
  <interceptor-ref name="execAndWait"/> 
        
  <result name="wait">/wait.jsp</result> 
  <result name="error">/error.jsp</result>
  <result name="success">/success.jsp</result>
</action>
LongLiveAction.java

public class LongLivedAction {
	private String username;
	private String password;
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	
	@SuppressWarnings("unchecked")
	public String execute() throws Exception{
		addToSessionScope("user", getUsername());
		
		// Just for test of token.
		System.out.println( "name:" + getUsername() + "\tpassword:" + getPassword());
		consumeTimeForToken();
		
		if( getUsername().equals("gqltt") && getPassword().equals("123")){
			//addToSessionScope("user", getUsername());
			return "success";
		}
		
		return "error";
	}
	
	@SuppressWarnings("unchecked")
	void addToSessionScope( String key, String value ){
		Map session = ActionContext.getContext().getSession();
		if( session == null ){
			System.out.println("Error: session is null...");
			return ;
		}
		session.put(key, value);
		//ActionContext.getContext().getSession().put(key, value);
	}
	
	//消耗时间
	void consumeTimeForToken(){
		try {
			Thread.sleep( 4*1000 );
		} catch (InterruptedException e) {
			// do nothing...
		}
		System.out.println("After 4 seconds...");
	}
}
wait.jsp

注意:不要忘了加入 <%@ taglib prefix="s" uri="/struts-tags"%>

<%@ page language="java" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>Please Wait</title>
	<meta http-equiv="refresh" content="3;url=<s:url includeParams='all'/> "/> 
	
  </head>
  
  <body>
   Please Wait...
  </body>
</html>

缺点:将参数写在 URL 中,明文化了(密码都看得到了!!!)



问题:取不到 session,Map session = ActionContext.getContext().getSession(); 操作一直返回 Null。为什么?


参考:http://webservices.ctocio.com.cn/java/470/9189470.shtml

下面是另一种,等待画面的配置,可以解决 session 为 Null 的问题:

struts.xml 添加配置:

		<action name="Wait" class="com.gq.WaitAction"> 
			<interceptor-ref name="defaultStack"/>
      		<interceptor-ref name="execAndWait"> 
      			<param name="excludeMethods">input</param> 
      			
      			<!-- 
      			等待时间,执行时间没有超过此值,将不显示等待画面 (毫秒) 
      			<param name="delay">1000</param>
      			--> 
      			
      			<!-- 
      			间隔检查时间,检查后台进程有没有执行完毕,如果完成了它就立刻返回,
      			不用等到等待,用户不会看到等待画面  
      			<param name="delaySleepInterval">50</param>
      			--> 
      		</interceptor-ref> 
      		
      		<result name="wait">/wait.jsp</result> 
			<result name="error">/error.jsp</result>
			<result name="success">/success.jsp</result>
		</action>

WaitAction.java

public class WaitAction extends ActionSupport implements SessionAware {
	private static final long serialVersionUID = -5724238080200557097L;
	
	private Map session;
	public void setSession(Map session) {
		this.session = session;
	}
	public Map getSession(){
		return session;
	}
	
	private String username;
	private String password;
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	
	@SuppressWarnings("unchecked")
	public String execute() throws Exception{
		System.out.println("In Wait.action");
		addToSessionScope("user", getUsername());
		
		// Just for test of token.
		System.out.println( "name:" + getUsername() + "\tpassword:" + getPassword());
		consumeTimeForToken();
		
		if( getUsername().equals("gqltt") && getPassword().equals("123")){
			//addToSessionScope("user", getUsername());
			return "success";
		}
		
		return "error";
	}
	
	@SuppressWarnings("unchecked")
	void addToSessionScope( String key, String value ){
		getSession().put(key, value);
	}
	
	//消耗时间
	void consumeTimeForToken(){
		try {
			Thread.sleep( 4*1000 );
		} catch (InterruptedException e) {
			// do nothing...
		}
		System.out.println("After 4 seconds...");
	}
}
注意:Action 要实现 SessionAware接口(验证过——这样的添加属性到 session 是OK 的!)

因为这个action将会以单独的线程执行,所以你不能用ActionContext,因为它是ThreadLocal.这也就是说如果你要访问session数据,你必须实现 SessionAware结构而不是调用ActionContext.getSesion() 。


wait.jsp(带刷新失败时候的超链接)

<%@page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>   
<%@taglib prefix="s"uri="/struts-tags"%>   
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">   
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">   
<meta http-equiv="refresh" content="1;url=<s:url includeParams="none" />"/>   
<title> 
</title> 
</head> 
<body> 
<h1>数据处理中,请稍等......</h1> 
如果没有自动跳转请<a href="<s:url includeParams="all" />">点这里</a>. 
</body> 
</html>
其中的includeParams参数取值为:
none,不把参数加入到url参数中 
all,是把get和post中的参数加入到url参数中
get,是只把get中的参数加入到url参数中 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值