复习_Struts2_result标签、访问servlet的api、请求参数的接收机制

1.result标签

在sturts.xml文件中,result标签的配置非常简单,使用<result>元素来配置result逻辑视图与物理视图之间的映射,<result>元素可以有name和type属性,但这两种属性都不是必选的,如果不配置都会使用其默认配置。

作用: 为动作指定结果集视图。
属性:

  • name:逻辑视图的名称,对应着动作方法的返回值。默认值是success。
  • type:结果集类型,指的就是用什么方式转到定义的页面。默认是dispatcher(请求转发)。
    type的常用取值:
    • dispatcher:(默认值) 使用请求转发,转向一个页面。
    • redirect:使用重定向,转向一个页面。
    • redirectAction:使用重定向,转向另外一个action
1.1 结果集类型

在Struts2中,当框架调用Action对请求进行处理后,就要向用户呈现一个结果视图。在Struts2中,预定义了多种的结果集类型。
一个结果集类型就是实现了com.opensymphony.xwork2.Result接口的类,Struts2把内置的<result-type>都放在struts-default包中,sturts-default包就是配置包的父包,这个包定义在struts2-core-2.3.24.jar包中的根目录下的struts-default.xml文件中,可以找到相关的<result-type>的定义。
在这里插入图片描述
每个<result-type>元素都是一种视图技术或者跳转方式的封装,其中的name属性指出在<result>元素中如何引用这种视图技术或者跳转方式,对应着<result>元素的type属性。Struts2中预定义的ResultType如表所示。
在这里插入图片描述
其中红色的几个值比较常用,需要重点记忆。其他的了解即可。

1.2 结果集:dispatcher

示例】
测试常用的结果集类型dispatcher、redirect、redirectAction
需求:创建三个页面,index.jsp、success.jsp和errorPage.jsp分别演示请求转发、请求重定向以及重定向到action。

步骤一:创建工程
在这里插入图片描述
步骤二:导入jar包、配置前端控制器
在这里插入图片描述

<filter>
	<filter-name>struts2</filter-name>
	<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>struts2</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

创建页面

success.jsp:
<body>
	<h1>执行成功!</h1>
</body>

erroPage.jsp:
<body>
	<h1>执行失败!</h1>
</body>
index.jsp:
<body>
	<a href="${pageContext.request.contextPath }/hello">访问hello</a>
</body>

配置struts.xml

<struts>
	<!-- 配置开发者模式 -->
	<constant name="struts.devMode" value="true"></constant>
	<!-- 配置包 -->
	<package name="p1" extends="struts-default">
		<action name="hello" class="cn.itcast.struts.HelloAction" method="hello">
			<!-- 
				结果集的配置方式:
				name:视图名称分为逻辑视图名称和物理视图名称
					success是逻辑视图名称(需要判断是否和action中返回的视图名称一致),/success.jsp是物理视图名称
			 	type:结果集的类型
			 	常用配置:
			 		dispatcher:请求转发
			 			一次请求、服务器端的行为、地址栏不变、request生命周期被延长
			 		redirect:请求重定向
			 			二次请求、浏览器的行为、地址栏发生改变、request生命周期在第一次 请求结束时结束
			 		redirectAction:请求重定向到Action
			 			二次请求、第二次会将请求重定向到一个动作类
			 
			 -->
			<result name="success" type="dispatcher">/success.jsp</result>
			<result name="error" type="redirect">/errorPage.jsp</result>
			<result name="hello2" type="redirectAction">hello2</result>
		</action>
		
		<action name="hello2">
			<result name="success">/success.jsp</result>
		</action>
	</package>
</struts>
1.3 结果集:redirect

1.4 结果集:redirectAction

1.5 redirect和redirectAction的区别

使用redirect也可以重定向到helloAction这个动作类

将重定向到action的结果集类型修改成redirect后测试
原:
<result name="hello2" type="redirectAction">hello2</result>
修改后
<result name="hello2" type="redirect">hello2</result>

步骤二:测试
地址栏发送请求:http://localhost:8080/struts2_day02/hello
在这里插入图片描述
思考:不管是redirect还是redirectAction都可以重定向到hello2的,有什么区别?
区别是:
redirectAction重定向后请求带.action的后缀名称
在这里插入图片描述
redirect重定向后请求不带.action的后缀名称
在这里插入图片描述
作用:当用户固定了请求的扩展名的常量后redirect就无法重定向到一个动作类

<!-- 添加后缀名称为.do -->
<constant name="struts.action.extension" value="do"></constant>

步骤四:请求测试

注意:请求时后缀名称要加.do,如:
http://localhost:8080/struts2_day02/hello.do
测试发现,第二次重定向到action(即动作类)发生错误。

public class HelloAction extends ActionSupport {

    public String t1(){
        System.out.println("hello方法执行了......");
        return "h1";
    }

    public String t2(){
        System.out.println("hello方法执行了......");
        return "h2";
    }

}
<package name="22p1" extends="struts-default">
    <action name="*" class="cn.itcast.action.HelloAction" method="{1}">
        <result name="h1" type="redirect">hello2</result>
        <result name="h2" type="redirectAction">hello2</result>
    </action>

    <action name="hello2">
        <result name="success">/success.jsp</result>
    </action>
</package>

当我们请求t1时http://localhost:8080/Struts2/t1.do,寻找到result为h1,由于是redirect,在重定向时不会加后缀,所以最终请求为:http://localhost:8080/Struts2/hello2
而请求t2.do时,最终请求路径为http://localhost:8080/Struts2/hello2.do,请求时会自动添加一个后缀名称。


2.全局结果集和局部结果集

局部结果视图和全局结果视图:
配置在action标签内的result,我们称之为局部结果视图,它只能由当前action使用。
而在实际开发中,有很多页面,每个action可能都会用到。
比如:success.jsp,error.jsp,login.jsp等等。
当我们很多action都用到了login.jsp,在每个action标签中都配置一次,显然是不合理的,这个时候我们就用到了全局结果视图。
全局结果集的作用范围:整个包下所有的action都可以使用。

2.1 局部结果集的使用

注意:编写示例之前先将后缀名的常量注释掉以免以免后面以.action为后缀名或者没有后缀名的请求因为找不到资源而报404。

<constant name="struts.action.extension" value="do"></constant>

局部结果集:只在当前的action中起作用

【示例】
需求:创建一个index.jsp页面和一个DemoAction,通过该页面上的a标签来访问DemoAction.

创建index.jsp

<body>
	<a href="${pageContext.request.contextPath }/demo">访问demo</a>
</body>

编写action

public class DemoAction extends ActionSupport{
	public String demo(){
		System.out.println("demo方法执行了......");
		return SUCCESS;
	}
}

struts.xml

<action name="demo" class="cn.itcast.struts.DemoAction" method="demo">
	<result name="success">/success.jsp</result>
</action>

点击a标签后,访问成功

问: 此时在HelloAction和DemoAction中配置了相同的success结果集,那么是否可以通用呢?
在这里插入图片描述
步骤四:将DemoAction中的结果集注释掉后再次测试访问
在这里插入图片描述
访问结果:报错
在这里插入图片描述
原因:
DemoAction中的结果集和HelloAction的的结果集虽然相同,但是无法共用,当前action中的结果集只能在当前action有效,这就是局部结果集。

问题:如果有多个action中都需要使用同一个success结果集视图,就需要配置多个result标签,太麻烦,是否有其他解决方案?
答案:配置全局结果集,可以使多个action共用同一个结果集视图。

2.2 全局结果集的使用

全局结果集:在当前package包中起作用,该包中所有的action可以共用该结果集。

配置一个全局的结果集,然后将局部结果集都注释掉

<package name="p1" extends="struts-default">
	<!-- 配置全局结果集 -->
	<global-results>
		<result name="success">/success.jsp</result>
	</global-results>
	
	<action name="hello" class="cn.itcast.struts.HelloAction" method="hello">
		<!-- <result name="success" type="dispatcher">/success.jsp</result> -->
		<result name="error" type="redirect">/errorPage.jsp</result>
		<result name="hello2" type="redirectAction">hello2</result>
	</action>
	
	<action name="hello2">
		<result name="success">/success.jsp</result>
	</action>
	
	<action name="demo" class="cn.itcast.struts.DemoAction" method="demo">
		<!-- <result name="success">/success.jsp</result> -->
	</action>
</package>
2.3 全局结果集和局部结果集的优先级

注意:如果局部结果集和全局结果集都配置了相同的逻辑结果集视图时,action在执行时,会先找局部结果集视图,没找到再找全局结果集视图的。


3.访问Servlet的API

前面已经对Struts2的流程已经执行完成了,但是如果表单中有参数如何进行接收又或者我们需要向页面保存一些数据,又要如何完成呢?我们可以通过学习Struts2访问Servlet的API来实现这样的功能。

在Struts2中,Action并没有直接和Servlet API进行耦合,也就是说在Struts2的Action中不能直接访问Servlet API。虽然Struts2中的Action访问Servlet API麻烦一些,但是这却是Struts2中Action的重要改良之一,方便Action进行单元测试。
尽管Action和Servlet API解耦会带来很多好处,然而在Action中完全不访问Servlet API几乎是不可能的,在实现业务逻辑时,经常要访问Servlet中的对象,如session、request和application等。在Struts2中,访问Servlet API有2种方法,具体如下:

  • 静态方法调用servlet相关API-重点
  • 接口注入方式调用servlet相关API-了解
  • 通过ActionContext来调用-了解
3.1 静态方法调用-重点

通过struts2框架提供的ServletActionContext工具类来调用servlet相关API
【示例】
使用ServletActionContext获取request、response、application、session

在index.jsp页面中添加一个a标签

<a href="${pageContext.request.contextPath }/demo2">访问demo2</a><br>

编写action

public class Demo2Action extends ActionSupport {
	public String demo2(){
		//获取request
		HttpServletRequest request = ServletActionContext.getRequest();
		//获取session
		HttpSession session = request.getSession();
		//获取response
		HttpServletResponse response = ServletActionContext.getResponse();
		//获取application
		ServletContext context = ServletActionContext.getServletContext();
		return SUCCESS;
	}
}

配置struts.xml

<action name="demo2" class="cn.itcast.struts.Demo2Action" method="demo2">
	<result name="success">/success.jsp</result>
</action>
3.2 接口注入方式-了解

通过实现以下接口,获取request、response、session、application
获取Context需要实现ServletContextAware
获取Request需要实现ServletRequestAware
获取Response需要实现ServletResponseAware

步骤一:修改页面

<body>
	<a href="${pageContext.request.contextPath }/demo">访问demo</a><br>
	<a href="${pageContext.request.contextPath }/demo2">访问demo2</a><br>
	<a href="${pageContext.request.contextPath }/demo3">访问demo3</a><br>
</body>

编写action

public class Demo3Action extends ActionSupport implements ServletRequestAware,ServletResponseAware,ServletContextAware{
	private HttpServletRequest request;
	private HttpServletResponse response;
	private HttpSession session;
	private ServletContext application;
	public String demo3(){
		System.out.println("demo3执行了......");
		return SUCCESS;
	}

	@Override
	public void setServletContext(ServletContext context) {
		this.application=context;
		
	}

	@Override
	public void setServletResponse(HttpServletResponse response) {
		this.response = response;
		
	}

	@Override
	public void setServletRequest(HttpServletRequest request) {
		this.request = request;
		this.session = request.getSession();
	}
}

步骤三:struts.xml

<action name="demo3" class="cn.itcast.struts.Demo3Action" method="demo3">
	<result name="success">/success.jsp</result>
</action>

问题:只有调用了action中的set方法才能完成request、response等对象的传参并赋值,那么是由谁来调用这些方法的呢?
答:拦截器-servletConfig
在struts-default.xml中配置了一个默认的拦截器栈

<default-interceptor-ref name="defaultStack"/>

在该拦截器栈中有一个<interceptor-ref name=“servletConfig”/>会调用action中实现了接口后复写的set方法。


4.请求参数的接收机制

表现层有一个核心职责 ,负责接收客户端提交请求数据并响应

  • 表现层 负责和客户端交互 (围绕 request 、response )
  • 业务层 负责数据业务逻辑处理
  • 持久层 负责和数据库进行增删改查

Struts2提供两大类(属性驱动、模型驱动)、三种数据封装的方式:

  • 属性驱动方式
    • 没有实体类的:通过成员变量的set(setUsername(Sting username))方法进行封装。
    • 有实体类的:页面通过对象.属性的方式来提交数据。Action中通过实体类中属性的set、get方法封装和获取数据。
  • 模型驱动方式:
    • 使用ModelDriven接口(模型驱动),对请求的数据进行封装。
4.1 属性驱动:没有实体类

通过表单中属性名称的set方法进行参数的封装。

<body>
	<form action="${pageContext.request.contextPath }/param1" method="post">
		用户名:<input type="text" name="username"/>
		年龄:<input type="text" name="age"/>
		<input type="submit" value="提交">
	</form>
</body>

编写action

/**
 * 属性驱动:没有实体
 * 	要求:在成员变量的位置定义表单提交的参数,并且设置参数的set方法
 * 	执行过程:通过参数的set方法将数据进行封装
 * 	参数的封装是通过params拦截器完成的
 * @author Administrator
 *
 */
public class Param1Action extends ActionSupport{
	private String username;
	private int age;
	

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


	public void setAge(int age) {
		this.age = age;
	}


	public String param1(){
		System.out.println(username+"..."+age);
		return SUCCESS;
	}
}

struts.xml

<action name="param1" class="cn.itcast.struts.Param1Action" method="param1">
		<result name="success">/success.jsp</result>
</action>

在这里插入图片描述

4.2 属性驱动:有实体类

上面的封装方式,如果在参数少的情况下可以使用,如果参数太多的话,需要手动编写成员变量和设置set方法就比较麻烦了,因此可以使用实体类让参数进行自动封装。

对象名.属性

<form action="${pageContext.request.contextPath }/param2" method="post">
	<!-- 需要通过对象.属性的方式提交数据 -->
	用户名:<input type="text" name="user.username"/>
	年龄:<input type="text" name="user.age"/>
	<input type="submit" value="提交">
</form>

编写实体类

public class User {
	private String username;
	private int age;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [username=" + username + ", age=" + age + "]";
	}
	
}

编写action

/**
 * 属性驱动:有实体类
 * 要求:
 * 	1、表单需要以对象.属性的方式提交数据
 * 	2、action中的实体类必须有get和set方法
 * 执行过程:
 * 		通过实体类的get方法获取user对象,判断user是否为null
 * 			如果为null:
 * 				初始化一个user对象,通过user的set方法,将user对象传递到action。
 * 				然后通过user对象中属性的set方法将age参数传递并封装
 * 				然后再次通过user的get方法获取封装了参数的user对象,然后再通过user中属性的set方法对username进行参数的封装。
 * 			如果不为null:
 * 				就直接通过user对象中age的set方法将数据进行封装
 * 				然后再次通过user的get方法获取封装了age的user对象,再通过该user对象中username的set方法将参数进行封装和传递。
 * 	注意:表单必须以对象.属性的方式提交数据。
 * @author Administrator
 *
 */
public class Param2Action extends ActionSupport {
	private User user = new User();

	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}
	public String param2(){
		System.out.println(user);
		return SUCCESS;
	}
}

步骤四:struts.xml

<action name="param2" class="cn.itcast.struts.Param2Action" method="param2">
	<result name="success">/success.jsp</result>
</action>

问题:如果使用属性驱动中的实体类来进行参数的封装,表单中的所有属性都必须通过对象.属性的方式提交数据,写起来比较麻烦。

4.3 模型驱动

注意:在复制表单时,不要把第二个表单做例子user.username

修改页面

<form action="${pageContext.request.contextPath }/param3" method="post">
	<!-- 需要通过对象.属性的方式提交数据 -->
	用户名:<input type="text" name="username"/>
	年龄:<input type="text" name="age"/>
	<input type="submit" value="提交">
</form>

编写action

/**
 * 模型驱动:
 *  1、选哟实现ModelDriven接口 
 *  2、需要初始化对象 
 *  3、需要复写接口方法
 * 
 * 最终还是通过params拦截器实现数据封装,不过之前还需要modelDriven拦截器的支持。
 * @author Administrator
 *
 */
public class Param3Action extends ActionSupport implements ModelDriven<User> {

	private User user = new User();
	@Override
	public User getModel() {
		return user;
	}

	public String param3() {
		System.out.println(user);
		return SUCCESS;
	}

}

struts.xml

<action name="param3" class="cn.itcast.struts.Param3Action" method="param3">
	<result name="success">/success.jsp</result>
</action>
4.4 内置的类型转换器

从上面的案例中,我们可以发现,在action中定义了一个Integer类型的age直接可以接收表单提交的年龄。
有一点我们非常肯定:页面传递过来的参数肯定是String类型,为什么我们可以使用Integer类型接收呢?

【解答】Struts2提供了功能非常强大的类型转换器,用于将请求数据进行自动转换。
对于大部分常用类型,开发者根本无需创建自己的转换器。

Struts2内置了常见数据类型多种转换器,如下:

  • boolean 和 Boolean
  • char和 Character
  • int 和 Integer
  • long 和 Long
  • float 和 Float
  • double 和 Double
  • Date 可以接收 yyyy-MM-dd格式字符串
  • 数组 可以将多个同名参数,转换到数组中
  • 集合 支持将数据保存到 List 或者 Map 集合

【示例】修改页面

<form action="${pageContext.request.contextPath }/param4" method="post">
	<!-- 需要通过对象.属性的方式提交数据 -->
	用户名:<input type="text" name="username"/>
	年龄:<input type="text" name="age"/>
	生日:<input type="text" name="birth">
	兴趣爱好:<input type="checkbox" name="hobby" value="睡觉">睡觉
			<input type="checkbox" name="hobby" value="旅游">旅游
			<input type="checkbox" name="hobby" value="上网">上网
			<input type="checkbox" name="hobby" value="健身">健身
	<input type="submit" value="提交">
</form>

修改实体类

public class User {
	private String username;
	private Integer age;
	private Date birth;
	private String [] hobby;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public Date getBirth() {
		return birth;
	}
	public void setBirth(Date birth) {
		this.birth = birth;
	}
	public String[] getHobby() {
		return hobby;
	}
	public void setHobby(String[] hobby) {
		this.hobby = hobby;
	}
	@Override
	public String toString() {
		return "User [username=" + username + ", age=" + age + ", birth="
				+ birth + ", hobby=" + Arrays.toString(hobby) + "]";
	}
	
}

编写action

public class Param4Action extends ActionSupport implements ModelDriven<User>{
	private User user = new User();
	@Override
	public User getModel() {
		return user;
	}
	
	public String param4(){
		System.out.println(user);
		return SUCCESS;
	}

}

struts.xml

<action name="param4" class="cn.itcast.struts.Param4Action" method="param4">
	<result name="success">/success.jsp</result>
</action>

注意:如果兴趣爱好使用的是字符串进行接收,那么会将多个字符串通过逗号+空格的方式来进行拼接,如:睡觉, 旅游, 上网, 健身

封装到list
注意:users[0].username中的users必须和action中的集合名称一致。
其封装过程和属性驱动:有实体类的封装封装雷同。

修改页面

<form action="${pageContext.request.contextPath }/param5" method="post">
	<!-- 需要通过对象.属性的方式提交数据 -->
	用户名1:<input type="text" name="users[0].username"/>
	年龄1:<input type="text" name="users[0].age"/><br>
	用户名2:<input type="text" name="users[1].username"/>
	年龄2:<input type="text" name="users[1].age"/><br>
	用户名3:<input type="text" name="users[2].username"/>
	年龄3:<input type="text" name="users[2].age"/><br>
	<input type="submit" value="提交">
</form>

编写struts.xml

<action name="param5" class="cn.itcast.struts.Param5Action" method="param5">
	<result name="success">/success.jsp</result>
</action>

编写action

public class Param5Action extends ActionSupport{
	private List<User> users;

	public List<User> getUsers() {
		return users;
	}
	
	
	public void setUsers(List<User> users) {
		this.users = users;
	}


	public String param5(){
		System.out.println(users);
		return SUCCESS;
	}

}

封装到map
通过users[‘key1’].username的方式在页面上提交数据
最终封装后的数据为:
{key1=User [username=zhangsan, age=12]}

修改页面

<form action="${pageContext.request.contextPath }/param6" method="post">
	<!-- 需要通过对象.属性的方式提交数据 -->
	用户名1:<input type="text" name="users['key1'].username"/>
	年龄1:<input type="text" name="users['key1'].age"/><br>
	用户名2:<input type="text" name="users['key2'].username"/>
	年龄2:<input type="text" name="users['key2'].age"/><br>
	用户名3:<input type="text" name="users['key3'].username"/>
	年龄3:<input type="text" name="users['key3'].age"/><br>
	<input type="submit" value="提交">
</form>

编写struts.xml

<action name="param6" class="cn.itcast.struts.Param6Action" method="param6">
	<result name="success">/success.jsp</result>
</action>

编写action

public class Param6Action extends ActionSupport{
	private Map<String,User> users;

	public Map<String,User> getUsers() {
		return users;
	}

	public void setUsers(Map<String,User> users) {
		this.users = users;
	}

	public String param6(){
		System.out.println(users);
		return SUCCESS;
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值