5、struts2访问Servlet API及web应用单元测试、结果类型及项目开发模式

在struts框架下,如何访问servlet

通过ServletActionContext类,其父类ActionContext

在Action的execute方法中调用session:

public String execute() throws Exception
	{
		
//		if(!"hello".equals(username))
//		{
//			throw new UsernameException("username invalid");
//		}
//		if(!"world".equals(password))
//		{
//			throw new PasswordException("password invalid!");
//		}
		
		HttpServletRequest request = ServletActionContext.getRequest();
		HttpSession session = request.getSession();
		session.setAttribute("hello", "heloworld");
		
		ActionContext actionContext = ActionContext.getContext();
		Map map = actionContext.getSession();
		//使用Map是为了进行单元测试
		Object object = map.get("hello");
		System.out.println(object);
		
		
		if(this.loginService.isLogin(username, password))
		{
			return SUCCESS;
		}
		
		return INPUT;
	}


 

服务器端代码的单元测试有两种模式:

1)容器内测试(Jetty)
2)Mock测试(继承HttpServletRequest、HttpSession、HttpServletResponse等Servlet API)。JMock,EasyMock

Preparable接口的作用是让Action完成一些初始化工作,这些初始化工作是放在Preparable接口的prepare方法中完成的,该方法会在execute方法执行之前得到调用。

struts-default.xml,struts框架的缺省部署






    
    

    

    
    

    
    

    
    
    

    
    
    

    
    
    
    

    
    
    

    

    
    

    
    
    
    

    
    
    
    
    
    

    

    
    
    
    
    

    
    

    
    

    
    
    

    
    

    
    

    
    

    

    

    
    
    
    
    
    
    
    
    

    
    

    

    
    

    
    
    
    

    
        
            
            
            
            
            
            
            
            
            
            
        

        
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            

            
            
                
                
                
                
                
                
                
                    dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*
                
                
            

            
            
                
                
                
            

            
            
                
                
            

            
            
                
                
            

            
            
                
                
            

            
            
                
                
            

            
            
                
                
                
                
                
                
                    dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*
                
                
                
                
                
                
                
                
                
                    dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*
                
                
                
                    input,back,cancel,browse
                
                
                    input,back,cancel,browse
                
            

            
            
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                    dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*
                
                
                
                    input,back,cancel,browse
                
                
                    input,back,cancel,browse
                
                
            

            
            
                
            

            
            
                
                    input,back,cancel
                
                
                
                    input,back,cancel
                
            

       

        

        
    



action标签下的子标签result可以有type属性,其值是在struts-default.xml中枚举出来的,就是标签中列出的类型。

dispatcher和redirectAction的区别,dispatcher是请求转发,redirectAction是重定向

采取请求转发的方式完成表单内容的添加会造成内容的重复插入。

5、struts2访问Servlet API及web应用单元测试、结果类型及项目开发模式_第1张图片

一个添加表记录的页面add,输入信息后提交给一个servlet,servlet执行记录插入操作add后,执行list查询,然后将结果在list页面呈现,如果使用list页面使用请求转发方式,那么这是的list页面地址栏显示的是servlet地址,这时如果刷新list页面,相当于重复提交一次插入操作,造成内容的重复插入。使用重定向的方式添加数据则不存在这种数据的重复插入问题,是实际开发中应该注意的问题。

5、struts2访问Servlet API及web应用单元测试、结果类型及项目开发模式_第2张图片

 

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

1)通过重定向
2)通过Session Token(Session令牌)

使用redirectAction进行重定向,同时传递参数:

一个输入页面action.jsp

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



  
    
    
    My JSP 'action.jsp' starting page
    
	
	
	    
	
	
	

  
  
  
   
username:
password:


编写两个Action:Action1和Action2

package com.cdtax.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class Action1 extends ActionSupport
{
	private String username;
	private String password;
	
	private String usernameAndpassword;
	
	public String getUsername()
	{
		return username;
	}
	public void setUsername(String username)
	{
		this.username = username;
	}
	public String getPassword()
	{
		return password;
	}
	public void setPassword(String password)
	{
		this.password = password;
	}
	

	public String getUsernameAndpassword()
	{
		return usernameAndpassword;
	}
	public void setUsernameAndpassword(String usernameAndpassword)
	{
		this.usernameAndpassword = usernameAndpassword;
	}
	@Override
	public String execute() throws Exception
	{
		this.usernameAndpassword = username + password;
		System.out.println("action1:  " + usernameAndpassword);
		return SUCCESS;
	}
}


 

package com.cdtax.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class Action2 extends ActionSupport
{
	private String username;
	private String password;
	
	private String usernameAndpassword;
	
	public String getUsername()
	{
		return username;
	}

	public void setUsername(String username)
	{
		this.username = username;
	}

	public String getPassword()
	{
		return password;
	}

	public void setPassword(String password)
	{
		this.password = password;
	}

	public String getUsernameAndpassword()
	{
		return usernameAndpassword;
	}

	public void setUsernameAndpassword(String usernameAndpassword)
	{
		this.usernameAndpassword = usernameAndpassword;
	}

	@Override
	public String execute() throws Exception
	{
		System.out.println("action2" + usernameAndpassword);
		return SUCCESS;
	}
}


配置struts.xml:


	/action2.jsp


	
		action22
		${username}
		${password}
		${usernameAndpassword}
				

最后的显示页面action2.jsp

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



  
    
    
    My JSP 'action2.jsp' starting page
    
	
	
	    
	
	
	

  
  
  
	username:
password:
usernameAndpassword:


主要注意struts.xml中的action配置,使用result标签属性type使用redirectAction,表示action1执行成功后(返回success)要重定向到另一个action,并且可以传递参数,参数使用${ }的格式,其中name指出传递参数的变量名(应该与action2中的成员变量名一致),${}花括号中为本action中的成员变量名,如action1中的username。还可以传递提交页面中没有的变量,这里可以做一个实验,将name=“username”改为name=“abcde”,在action2中将getUsername和setUsername改为getAbcde和setAbcde,输出页面action2.jsp增加abcde:,其他不变,输入aaa,12345,则输出的结果为

username:
password:12345
usernameAndpassword:aaa12345
abcde:aaa

看一下action2:

package com.cdtax.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class Action2 extends ActionSupport
{
	private String username="ooooo";
	private String password;
	
	private String usernameAndpassword;
	
	public String getAbcde()
	{
		return username;
	}

	public void setAbcde(String username)
	{
		this.username = username;
	}

	public String getPassword()
	{
		return password;
	}

	public void setPassword(String password)
	{
		this.password = password;
	}

	public String getUsernameAndpassword()
	{
		return usernameAndpassword;
	}

	public void setUsernameAndpassword(String usernameAndpassword)
	{
		this.usernameAndpassword = usernameAndpassword;
	}

	@Override
	public String execute() throws Exception
	{
		System.out.println("action2" + usernameAndpassword);
		System.out.println("action2:" + username);
		return SUCCESS;
	}
}


通过输出的结果,可以得出如下结论:

1)action设置入request的变量不是根据action的成员变量进行set的,而是根据action中get、set方法进行的,如有setUser,则request中就设置一个user,其值就是getUser的返回值,例如在action2中增加两个方法:

public String getUser()
{
    return "userceshi";
}

public void setUser()
{
   
}

虽然action2中没有定义user变量,但是在输出页面中增加user:,能够输出user:userceshi,即使去掉setUser方法也可以

2)action中各方法执行的顺序,将action1修改如下:

package com.cdtax.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class Action1 extends ActionSupport
{
	private String username;
	private String password;
	
	private String usernameAndpassword;
	
	public String getUsername()
	{
		System.out.println("get action1 username:  " + username);
		return username;
	}
	public void setUsername(String username)
	{
		System.out.println("set action1 username:  " + username);
		this.username = username;
	}
	public String getPassword()
	{
		System.out.println("get action1 password:  " + password);
		return password;
	}
	public void setPassword(String password)
	{
		System.out.println("set action1 password:  " + password);
		this.password = password;
	}
	

	public String getUsernameAndpassword()
	{
		System.out.println("get action1 userand pass:  " + usernameAndpassword);
		return usernameAndpassword;
	}
	public void setUsernameAndpassword(String usernameAndpassword)
	{
		System.out.println("set action1 userand pass:  " + usernameAndpassword);
		this.usernameAndpassword = username + password;
	}
	@Override
	public String execute() throws Exception
	{
//		this.usernameAndpassword = username + password;
		System.out.println("exe action1 userandpass:  " + usernameAndpassword);
		System.out.println("exe action1 username:  " + username);
		System.out.println("exe action1 password:  " + password);
		System.out.println("-------------");
		return SUCCESS;
	}
}


打印结果为:

set action1 password:  444
set action1 username:  ppppppppp
exe action1 userandpass:  null
exe action1 username:  ppppppppp
exe action1 password:  444
-------------
get action1 username:  ppppppppp
get action1 password:  444
get action1 userand pass:  null

也就是说,struts框架在调用action时,先调用set方法,应该是根据提交页面传来的参数,经过包装后,通过set方法赋给了action的成员变量,赋完值后,调用execute方法,该方法执行完毕后,在根据重定向需要的参数执行依次执行get方法。

大体的执行顺序如上,但是执行哪些get,set方法,参数是怎样在框架后台保存传递的还不清楚。

如我在上例action1中增加了一个成员变量String ceshi,生成相应的get和set方法,运行时这两个方法没有调用,如果在输入页面增加ceshi文本输入框,则set方法被调用,

如果在struts.xml的action里面result标签配置上${ceshi}则get方法被调用了。

也许是set方法是根据页面来的参数来调用的,get方法则是根据配置文件中要传递参数的使用情况调用的,测试:去掉这一行:

果然,getUsername方法没有被调用。

以上只是针对result 的type为redirectAction,其他是不是这样呢??

 

通过Session Token(Session令牌)防止表单重复提交:

1)首先是一个输入页面token.jsp,这个jsp要使用struts的标签库的form标签,并且form中要有一个token标签

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



  
    
    
    My JSP 'token.jsp' starting page
    
	
	
	    
	
	
	

  
  
  
    
    	username:
password:


然后编写action:

package com.cdtax.struts2;

import com.opensymphony.xwork2.ActionSupport;

public class TokenAction extends ActionSupport
{
	private String username;
	private String password;
	public String getUsername()
	{
		return username;
	}
	public void setUsername(String username)
	{
		this.username = username;
	}
	public String getPassword()
	{
		return password;
	}
	public void setPassword(String password)
	{
		this.password = password;
	}
	
	@Override
	public String execute() throws Exception
	{
		return SUCCESS;
	}
}


配置struts.xml:


	/tokenSuccess.jsp
	/tokenFail.jsp
	
	

要注意这里添加了两个result,第二个就是当重复提交时要转向的页面,然后是两个拦截器。

最后是2个结果页面:

tokenSuccess.jsp

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



  
    
    
    My JSP 'tokenSuccess.jsp' starting page
    
	
	
	    
	
	
	

  
  
  
    username:
    password:
  


tokenFail.jsp

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



  
    
    
    My JSP 'tokenFail.jsp' starting page
    
	
	
	    
	
	
	

  
  
  
    不要重复提交
  


session令牌的工作机理是这样的:当客户端请求页面时,服务器会通过token标签生成一个随机数,并且将该随机数放置到session当中,然后将该随机数发向客户端,通过查看客户端的源代码,我们发现token标签生成如下两个隐藏域:

如果客户第一次提交,那么会将该随机数发往服务器端,服务器会接收到该随机数并且与session中所保存的随机数进行比较,这时两者的值是相同的,服务器认为是第一次提交,并且将更新服务器端的这个随机数值;如果此时再次重复提交,那么客户端发向服务器端的随机数还是之前的那个,而服务器端的随机数则已经发生了变化,两者不同,服务器就认为这是重复提交,进而转向invalid.token所指向的结果页面。

5、struts2访问Servlet API及web应用单元测试、结果类型及项目开发模式_第3张图片

你可能感兴趣的:(Java,Struts)