在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是重定向
采取请求转发的方式完成表单内容的添加会造成内容的重复插入。
一个添加表记录的页面add,输入信息后提交给一个servlet,servlet执行记录插入操作add后,执行list查询,然后将结果在list页面呈现,如果使用list页面使用请求转发方式,那么这是的list页面地址栏显示的是servlet地址,这时如果刷新list页面,相当于重复提交一次插入操作,造成内容的重复插入。使用重定向的方式添加数据则不存在这种数据的重复插入问题,是实际开发中应该注意的问题。
防止表单重复提交的两种方式:
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
编写两个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,并且可以传递参数,参数使用
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:
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标签配置上
也许是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所指向的结果页面。