我是靠谱客的博主 朴实小刺猬,最近开发中收集的这篇文章主要介绍JAVAWEB开发之Struts2详解(二)——Action接受请求参数、类型转换器、使用Struts2的输入校验、以及遵守约定规则实现Struts2的零配置...,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Action接受请求参数

作为MVC框架,必须要负责解析HTTP请求参数,并将其封装到Model对象中
Struts2提供了非常强大的类型转换机制用于请求数据 到 model对象的封装
Struts2和MVC定义关系
StrutsPrepareAndExecuteFilter:控制器
在Struts2中action是什么?(Struts2是一个MVC框架)
V:jsp
M:action
C:action StrutsPrepareAndExecuteFilter

Struts2提供了三种数据封装方式:
  • Action本身作为model对象,通过成员setter封装。(属于属性驱动)
  • 创建独立model对象,页面通过ognl表达式封装。(属于属性驱动)
  • 使用ModelDrivern接口,对请求数据进行封装。(属于模型驱动)
具体使用如下:

1.属性驱动

属性驱动方式(一):直接将action作为一个model,就可以得到请求参数
action类成员变量setter接收参数如下图所示:

问题1:action封装请求参数,会不会存在线程安全问题?
是不会的,因为每一次请求,都是一个新的action
优点:使用简单
缺点:需要单独定义JavaBean,将action中属性copy到JavaBean中(不能将Action作为model传给service层)
这种方式,底层是通过反射进行实现的。

属性驱动方式(二):创建一个单独的Model类,在action类中引用model作为成员变量。(页面使用ognl)
具体操作:
在action类中声明一个mdoel,private User user;
提供对应的setter和getter方法。
在页面上使用ognl来进行描述
<input type="text" name="user.username"> 如下图所示:

这种方式的优点:简单易使用,解决了第一种封装的问题。
缺点:在页面上使用了ognl表达式,页面不通用了。
问题:这种方式,数据是怎样封装的?
是通过Struts2中name为params的interceptor拦截器进行的数据封装(Struts的核心core包下struts-default.xml中定义)<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>

2.模型驱动(在开发中应用比较多)

模型驱动的步骤:
1.让action 类实现ModelDrivern接口。
2.重写getModel方法
3.在action中实例化一个model对象,让getModel方法返回这个对象。
public class Login3Action extends ActionSupport implements ModelDriven<User> {
private User user = new User();
public User getModel() {
return user;
}

如下图所示:

优点:解决了属性驱动存在的问题
缺点:一次只能封装一个model对象
Struts2有很多围绕模型驱动的属性(在struts-default.xml中的拦截器中定义)
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
扩展:
(1)将数据封装到List集合。类型转换与Collection配合使用。
Struts2还允许填充Collection里的对象,这常见于需要快速录入批量数据的场合。

页面:
username1:<input type="text" name="users[0].username"><br>
password1:<input type="password" name="users[0].password"><br>
username2:<input type="text" name="users[1].username"><br>
password2:<input type="password" name="users[1].password"><br>

action类:
private List<User> users;
get/set方法
(2)将数据封装到Map集合(类型转换与Map配合使用)

页面:
username1:<input type="text" name="map['aaa'].username"><br>
password1:<input type="password" name="map['aaa'].password"><br>
username2:<input type="text" name="map['bbb'].username"><br>
password2:<input type="password" name="map['bbb'].password"><br>

action类:
private Map<String, User> map;
提供get/set
示例:
1.将action作为model(属性驱动方式一)
Login1Action
package cn.itcast.action;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
// 获取请求参数
属性驱动 第一种,直接将action作为model
public class Login1Action 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 {
HttpServletRequest request = ServletActionContext.getRequest();
// 2.判断用户名与密码是否正确
if ("tom".equals(username) && "123".equals(password)) {
request.getSession().setAttribute("username", username);
return SUCCESS;
} else {
request.setAttribute("login.message", "用户名或密码错误");
return "failer";
}
}
}
login1.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${requestScope["login.message"]}
<br>
<form action="${pageContext.request.contextPath}/login1" method="POST">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
2.将model对象作为Action类成员(属性驱动模式二)
封装Model类
User
package cn.itcast.domain;
public class User {
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 toString() {
return "User [username=" + username + ", password=" + password + "]";
}
}

Login2Action
package cn.itcast.action;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.ActionSupport;
// 获取请求参数
属性驱动 第二种,直接在action上声明一个model
public class Login2Action extends ActionSupport {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String execute() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
// 2.判断用户名与密码是否正确
if ("tom".equals(user.getUsername())
&& "123".equals(user.getPassword())) {
request.getSession().setAttribute("username", user.getUsername());
return SUCCESS;
} else {
request.setAttribute("login.message", "用户名或密码错误");
return "failer";
}
}
}
login2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${requestScope["login.message"]}
<br>
<form action="${pageContext.request.contextPath}/login2" method="POST">
username:<input type="text" name="user.username"><br>
password:<input type="password" name="user.password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
3.模型驱动(action类实现ModelDrivern接口)
Login3Action
package cn.itcast.action;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
// 获取请求参数 模型驱动
public class Login3Action extends ActionSupport implements ModelDriven<User> {
private User user=new User();
@Override
public User getModel() {
return user;
}
@Override
public String execute() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
// 2.判断用户名与密码是否正确
if ("tom".equals(user.getUsername())
&& "123".equals(user.getPassword())) {
request.getSession().setAttribute("username", user.getUsername());
return SUCCESS;
} else {
request.setAttribute("login.message", "用户名或密码错误");
return "failer";
}
}
}
login3.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${requestScope["login.message"]}
<br>
<form action="${pageContext.request.contextPath}/login3" method="POST">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>

success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${username }
</body>
</html>

4.将数据封装到List集合
list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/list" method="POST">
username:<input type="text" name="users[0].username"><br>
password:<input type="password" name="users[0].password"><br>
username:<input type="text" name="users[1].username"><br>
password:<input type="password" name="users[1].password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
ListAction
package cn.itcast.action;
import java.util.List;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.ActionSupport;
public class ListAction extends ActionSupport {
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
@Override
public String execute() throws Exception {
System.out.println(users);
return null;
}
}
5.将数据封装到Map集合
map.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/map" method="POST">
username:<input type="text" name="map['aaa'].username"><br>
password:<input type="password" name="map['aaa'].password"><br>
username:<input type="text" name="map['bbb'].username"><br>
password:<input type="password" name="map['bbb'].password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
MapAction
package cn.itcast.action;
import java.util.Map;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.ActionSupport;
public class MapAction extends ActionSupport {
private Map<String, User> map;
public Map<String, User> getMap() {
return map;
}
public void setMap(Map<String, User> map) {
this.map = map;
}
@Override
public String execute() throws Exception {
System.out.println(map);
return null;
}
}
配置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>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<package name="default" namespace="/" extends="struts-default">
<action name="login1" class="cn.itcast.action.Login1Action">
<result name="failer">/login1.jsp</result>
<result type="redirect">/success.jsp</result>
</action>
<action name="login2" class="cn.itcast.action.Login2Action">
<result name="failer">/login2.jsp</result>
<result type="redirect">/success.jsp</result>
</action>
<action name="login3" class="cn.itcast.action.Login3Action">
<result name="failer">/login3.jsp</result>
<result type="redirect">/success.jsp</result>
</action>
<action name="list" class="cn.itcast.action.ListAction"></action>
<action name="map" class="cn.itcast.action.MapAction"></action>
</package>
</struts>

验证属性驱动方式一:

验证属性驱动方式二:


验证模型驱动:

验证封装数据到List集合

验证封装数据到Map集合


2自定义类深入剖析属性驱动模式一和模型驱动

首先导入DOM4J、XPATH、BeanUtils的相关jar包

在src下新建struts.xml
新建自定义的ModelDrivern接口MyModelDrivern
package cn.itcast;
public interface MyModelDriven<T> {
public T getModel();
}
创建User类(和上面的一样)
......
创建Action类
package cn.itcast.action;
import cn.itcast.MyModelDriven;
import cn.itcast.User;
public class HelloAction implements MyModelDriven<User> {
// 剖析模型驱动
private User user = new User();
public User getModel() {
return user;
}
/*
属性驱动模式一
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;
}
*/
public String say() {
System.out.println(user.getUsername() + "
" + user.getPassword());
return "good";
}
}
自定义拦截器(模仿Struts中的params拦截器)
StrutsFilter
package cn.itcast.filter;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import cn.itcast.MyModelDriven;
public class StrutsFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
// 1.强转
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 2.操作
// 2.1 得到请求资源路径
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String path = uri.substring(contextPath.length() + 1);
// System.out.println(path); // hello
// 2.2 使用path去struts.xml文件中查找某一个<action name=path>这个标签
SAXReader reader = new SAXReader();
try {
// 得到struts.xml文件的document对象。
Document document = reader.read(new File(this.getClass()
.getResource("/struts.xml").getPath()));
Element actionElement = (Element) document
.selectSingleNode("//action[@name='" + path + "']"); // 查找<action
// name='hello'>这样的标签
if (actionElement != null) {
// 得到<action>标签上的class属性以及method属性
String className = actionElement.attributeValue("class"); // 得到了action类的名称
String methodName = actionElement.attributeValue("method");// 得到action类中的方法名称。
// 2.3通过反射,得到Class对象,得到Method对象
Class actionClass = Class.forName(className);
Method method = actionClass.getDeclaredMethod(methodName);
// 处理请求参数封装:
Object actionObj = actionClass.newInstance();
// 2.模型驱动
if (actionObj instanceof MyModelDriven) {
MyModelDriven mmd = (MyModelDriven) actionObj;
BeanUtils.populate(mmd.getModel(),
request.getParameterMap());
} else {
// 1.属性驱动
BeanUtils.populate(actionObj, request.getParameterMap());//
}
// 2.4 让method执行.
String returnValue = (String) method.invoke(actionObj); // 是让action类中的方法执行,并获取方法的返回值。
// 2.5
// 使用returnValue去action下查找其子元素result的name属性值,与returnValue做对比。
Element resultElement = actionElement.element("result");
String nameValue = resultElement.attributeValue("name");
if (returnValue.equals(nameValue)) {
// 2.6得到了要跳转的路径。
String skipPath = resultElement.getText();
// System.out.println(skipPath);
request.getRequestDispatcher(skipPath).forward(request,
response);
return;
}
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 3.放行
chain.doFilter(request, response);
}
public void destroy() {
}
}
编辑自定义的struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<struts>
<action name="hello" class="cn.itcast.action.HelloAction"
method="say">
<result name="good">/hello.jsp</result>
</action>
</struts>
在web.xml中配置拦截器
创建jsp进行测试:
hello.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<h1>hello Struts2</h1>
</body>
</html>
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/hello?username=tome&password=123">第一次使用struts2</a>
</body>
</html>

测试结果如下:

Struts2内置的类型转换器

类型转换:即在web中 使用Servlet时,使用BeanUtils直接将表单数据封装到JavaBean中。
对于大部分常用类型,开发者根本无需创建自己的转换器,Struts2内置了常见数据类型的多种转换器。
  • boolean和Boolean
  • char和Character
  • int和Integer
  • long和Long
  • float和Float
  • double和Double
  • Date可以接收yyyy-MM-dd格式字符串(浏览器语言首选项为中文时)
  • 数组 可以将多个同名参数转换到数组中
  • 集合 支持将数据保存到List或者Map集合
例如:日期类型,我们传递yyyy-MM-dd yyyy年MM月dd日 格式都可以,但是如果是yyyy/MM/dd就会出现问题。
关于Struts2中的类型转换器:
Struts2中的类型转换器根接口是:com.opensymphony.xwork.conversion.TypeConverter

自定义类型转换器

Struts2提供常规类型转换器,可以实现常用类型的转换,但是如果目标类型是一个特殊类型,则需要自定义转换器
Struts2 类型转换器实际上都是基于OGNL实现的,在OGNL项目中,有一个TypeConverter接口
自定义类型转换器必须实现 ongl.TypeConverter接口

相关的关键源代码如下:
TypeConverter接口关键源代码:

DefaultTypeConverter类实现了TypeConverter接口:


StrutsTypeConverter直接继承自DefaultTypeConverter:

自定义转换器的的步骤:
1.创建一个类实现TypeConverter接口
2.重写接口中方法,实现类型转换操作
3.注册类型转换器
详细说明:
1.创建一个自定义类型转换器(有三种方式)
方式一:实现TypeConverter需要重写
public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType);
如果实现接口,这个方法参数太多(6个)
方式二:不推荐实现接口,可以继承DefaultTypeConverter类
优点:重写的方法参数没有那么多
public Object convertValue(Map<String, Object> context, Object value, Class toType) {
return convertValue(value, toType);
}
方式三:(推荐使用)继承DefaultTypeConverter类的一个子类StrutsTypeConverter
public abstract Object convertFromString(Map context, String[] values, Class toClass);——请求封装
public abstract String convertToString(Map context, Object o);——数据回显
类型转换器一直都是双向转换:
页面提交的请求参数,封装到model——需要转换
model数据 需要在页面上回显——需要转换
2.怎样注册一个自定义类型转换器
注册方式有三种类型:
方式一:局部—针对于action
配置文件所在的位置及名称:在Action类所在包下,创建 Action类名-conversion.properties
配置文件书写:格式 属性名称=类型转换器的全类名
方式二:局部—针对于Model(属性驱动模式二)
配置文件所在位置以及名称:在Model类所在包,创建 Model类名-conversion.properties
配置文件书写:格式 属性名称=类型转换器的全类名
方式三:全局
配置文件所在位置以及名称:在src下创建一个 xwork-conversion.properties
配置文件书写 格式:要转换的类型全名=类型转换器的全类名

注意:
对于struts2中类型转换器,如果表单数据提交时,将数据向model封装,出现了问题,会报错:
No result defined for action cn.itcast.action.RegistAction and result input
上面的意思是说,在RegistAction的配置中没有配置input结果视图.
<action name="regist" class="cn.itcast.action.RegistAction">
<result name="input">/success.jsp</result>
</action>
如果配置了,出现类型转换问题,就会跳转到input指定的视图。

问题:为什么会向input视图跳转?

是因为struts2中的拦截器(interceptor).
在struts2中的conversionError拦截器用于记录类型转换问题
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
在struts2中的workflow拦截器用于得到问题,向input视图跳转。
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>

类型转换中的错误处理:
  • Struts2提供了一个名为conversionError的拦截器,查看struts-default.xml
  • <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
  • 如果Struts2的类型转换器执行类型转换时出现错误,该拦截器将负责将对应错误封装成表单域错误(FieldError),并将这些错误信息放入ActionContext中
  • 使用类型转换中的错误处理用户定义Action必须继承ActionSupport
  • 在自定义类型转换器中,异常必须抛出不能捕获,conversionError会处理该异常,然后转入名为input的逻辑视图
  • 在Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 :invalid.fieldvalue.属性名= 错误信息
  • 在input逻辑视图所对应jsp页面中,通过<s:fielderror/> 输出类型转换信息
程序示例如下:


首先新建自定义转换类MyDateConverter (通过继承自DefaultTypeConverter)
package cn.itcast.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import ognl.DefaultTypeConverter;
public class MyDateConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map context, Object value, Class toType) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
try {
if (toType == Date.class) {// 当字符串向Date类型转换时
String[] params = (String[]) value;
return dateFormat.parse(params[0]);
} else if (toType == String.class) {// 当Date转换成字符串时
Date date = (Date) value;
return dateFormat.format(date);
}
} catch (ParseException e) {
throw new RuntimeException("日期转换错误");
}
return null;
}
}
再新建一个日期转换类(使用另外一种方式继承自StrutsTypeConverter)
MyDateConverter2
package cn.itcast.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
public class MyDateConverter2 extends StrutsTypeConverter {
// 接收页面传递的数据封装到JavaBean
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
String value = values[0];
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date = null;
try {
date = sdf.parse(value);
} catch (ParseException e) {
// e.printStackTrace();
throw new RuntimeException();
}
return date;
}
@Override
public String convertToString(Map context, Object o) {
return null;
}
}
具体代码如下:
RegistAction
package cn.itcast.action1;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class RegistAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
@Override
public User getModel() {
return user;
}
@Override
public String execute() throws Exception {
System.out.println(user);
return null;
}
}

Regist2Action
package cn.itcast.action2;
import java.util.Arrays;
import java.util.Date;
import com.opensymphony.xwork2.ActionSupport;
public class Regist2Action extends ActionSupport {
private String username;
private String password;
private int age;
private Date birthday;
private String[] hobby;
@Override
public String toString() {
return "Regist2Action [username=" + username + ", password=" + password
+ ", age=" + age + ", birthday=" + birthday + ", hobby="
+ Arrays.toString(hobby) + "]";
}
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
@Override
public String execute() throws Exception {
System.out.println(this);
return null;
}
}

Regist2Action-conversion.properties
birthday=cn.itcast.utils.MyDateConverter2
User
package cn.itcast.domain;
import java.util.Arrays;
import java.util.Date;
public class User {
private String username;
private String password;
private int age;
private Date birthday;
private String[] hobby;
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password
+ ", age=" + age + ", birthday=" + birthday + ", hobby="
+ Arrays.toString(hobby) + "]";
}
}
User-conversion.properties
birthday=cn.itcast.utils.MyDateConverter

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>
<package name="default" namespace="/" extends="struts-default">
<action name="regist" class="cn.itcast.action1.RegistAction">
<result name="input">/success.jsp</result>
</action>
<action name="regist2" class="cn.itcast.action2.Regist2Action">
<result name="input">/success.jsp</result>
</action>
</package>
</struts>

regist.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/regist" method="POST">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
hobby:<input type="checkbox" name="hobby" value="eat">吃
<input type="checkbox" name="hobby" value="drink">喝
<input type="checkbox" name="hobby" value="play">玩<br>
age:<input type="text" name="age"><br>
birthday:<input type="text" name="birthday"><br>
<input type="submit" value="注册">
</form>
</body>
</html>
regist2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/regist2" method="POST">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
hobby:<input type="checkbox" name="hobby" value="eat">吃
<input type="checkbox" name="hobby" value="drink">喝
<input type="checkbox" name="hobby" value="play">玩<br>
age:<input type="text" name="age"><br>
birthday:<input type="text" name="birthday"><br>
<input type="submit" value="注册">
</form>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here</title>
</head>
<body>
<s:fielderror/>
</body>
</html>



Struts2的输入校验

校验的分类 : 客户端数据校验 和 服务器端数据校验
客户端数据校验 ,通过JavaScript 完成校验 (改善用户体验,使用户减少出错 )
服务器数据校验 ,使用框架内置校验功能 (struts2 内置校验功能 ) ----- 必须的

struts2 支持校验方式
代码校验 :在服务器端通过编写java代码,完成数据校验
配置校验 :XML配置校验(主流) 和 注解配置校验

代码校验(手动校验)了解

要求:action类必须继承自ActionSupport。需要重写一个validate方法
通过测试发现在action中的validate方法执行了。并且是在请求处理方法(execute)之前执行的。
对于struts2提供的校验,它也是通过拦截器实现的。

问题1:在validate方法中怎样存储错误校验信息?
在validate方法中 this.addFieldError(String name,String value);
问题2:在页面上怎样获取错误信息?(在Input视图上)
<s:fielderror> 展示所有的错误信息
<s:fielderror fieldName=""> 展示特定名称的错误信息
问题3:在同一个Action中有多个请求处理方法(login,regist) ,那么有些方法是需要校验的,有些是不需要的,如何处理?
解决方案:创建一个名称叫 validate+请求方法处理名(首字母大写) 例如:请求处理方法叫 regist() 校验 的方法名 validateRegist().会发现不管定义多少validateXxx方法 执行时都会走validate方法,所以可以将一些公共的校验放在validate方法中,可以把针对于特定方法的检验放在validateXxx方法下(xxx请求方法处理名)

手动校验的流程如下:
第一步:类型转换器对请求参数执行类型转换,并将转换后的值赋给action中的属性。
第二步:如果在执行转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息封装到fieldErrors里,然后执行第三步。如果类型转换没有出现异常,则直接进入第三步。
第三步:系统通过反射技术调用action中的ValidateXxx()方法,xxx为请求方法处理名。
第四步:调用action中的validate方法。
第五步:经过上面4步,如果系统中的fieldErrors存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。
手动校验示例如下:
导入Strutsjar包 配置好struts.xml
在WebRoot下新建login.jsp和regist.jsp
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<s:fielderror/>
<form action="${pageContext.request.contextPath}/login" method="post">
username:<input type="text" name="username"><s:fielderror fieldName="username.message"/><br>
password:<input type="password" name="password"><s:fielderror fieldName="password.message"/><br>
repassword:<input type="password" name="repassword"><br>
hobby:<input type="checkbox" name="hobby" value="eat">吃<input
type="checkbox" name="hobby" value="drink">喝<input
type="checkbox" name="hobby" value="play">玩<br> age:<input
type="text" name="age"><br> birthday:<input type="text"
name="birthday"><br>
email:<input type="text" name="email"><br>
url:<input type="text" name="url"><br>
telphone:<input type="text" name="telphone"><br>
<input type="submit" value="注册">
</form>
</body>
</html>

regist.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
<s:fielderror/>
<form action="${pageContext.request.contextPath}/regist" method="post">
username:<input type="text" name="username"><s:fielderror fieldName="username.message"/><br>
password:<input type="password" name="password"><s:fielderror fieldName="password.message"/><br>
repassword:<input type="password" name="repassword"><br>
hobby:<input type="checkbox" name="hobby" value="eat">吃<input
type="checkbox" name="hobby" value="drink">喝<input
type="checkbox" name="hobby" value="play">玩<br> age:<input
type="text" name="age"><br> birthday:<input type="text"
name="birthday"><br>
email:<input type="text" name="email"><br>
url:<input type="text" name="url"><br>
telphone:<input type="text" name="telphone"><br>
<input type="submit" value="注册">
</form>
</body>
</html>
新建model类User
package cn.itcast.domain;
import java.util.Arrays;
import java.util.Date;
public class User {
private String username;
private String password;
private int age;
private Date birthday;
private String[] hobby;
private String url;
private String email;
private String telphone;
private String repassword;
public String getRepassword() {
return repassword;
}
public void setRepassword(String repassword) {
this.repassword = repassword;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getTelphone() {
return telphone;
}
public void setTelphone(String telphone) {
this.telphone = telphone;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User [username=" + username + ", password=" + password
+ ", age=" + age + ", birthday=" + birthday + ", hobby="
+ Arrays.toString(hobby) + "]";
}
}

接着使用手动校验创建Action类UserAction
package cn.itcast.action;
import cn.itcast.domain.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
// 采用手动的方式
public class UserAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
@Override
public User getModel() {
return user;
}
@Override
public String execute() throws Exception {
System.out.println(user);
return null;
}
public String login() throws Exception {
System.out.println("login method ......");
return null;
}
public String regist() throws Exception {
System.out.println("regist method ......");
return null;
}
@Override
public void validate() {
System.out.println("validate method ......");
}
public void validateRegist() {
if (user.getUsername() == null
|| user.getUsername().trim().length() == 0) {
// 说明用户名为空
this.addFieldError("username.message", "用户名不能为空");
}
if (user.getPassword() == null
|| user.getPassword().trim().length() == 0) {
this.addFieldError("password.message", "密码不能为空");
}
if (!(user.getAge() >= 10 && user.getAge() <= 40)) {
this.addFieldError("age.message", "年龄必须在10-40之间");
}
System.out.println("validateRegist......");
}
}

配置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>
<package name="default" namespace="/" extends="struts-default">
<action name="regist" class="cn.itcast.action.UserAction"
method="regist">
<result name="input">/regist.jsp</result>
</action>
<action name="login" class="cn.itcast.action.UserAction"
method="login">
<result name="input">/login.jsp</result>
</action>
</package>
</struts>
输入http://localhost/mystruts2_day02_3/login.jsp 直接提交

发现首先调用了validate方法载调用了login请求处理方法

接着调试注册方法(提供了注册校验)
本地调试 http://localhost/mystruts2_day02_3/regist.jsp 直接提交

发现首先调用了validateRegist方法 再调用了validate方法
因此 只要重写了validate方法,不管怎样都会执行校验。所以可以把公共部分抽取到validate方法内。

配置校验

Struts2的校验框架已经完成了校验操作(做了很多校验法)而我们在使用时只需要调用它们即可(通过配置文件)
基于XML配置方式实现输入校验:
要求:Action类必须继承自ActionSupport或实现了Validateable接口。
在struts.xml中添加type为input的视图结果集。
添加配置文件。
关于配置文件的配置方式:
配置文件位置: xml文件要与Action类在同一包下。
配置文件名称:Action简单类名-validation.xml
配置文件约束:xwork-core-2.3.7.jar 中 xwork-validator-1.0.3.dtd 下
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">

配置文件的书写:
1.根元素:<validators>
2.子元素:<field name="属性名称"></field>
指定action中要校验的属性,name属性指定将被验证的表单字段的名称。
3.<field>的子元素
<field-validator type="校验器"> 是用于指定校验器
问题:校验器有哪些?
xwork-core-2.3.7.jar 中 /com/opensymphony/xwork2/validator/validators/default.xml下
<validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
<validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>
<validator name="int" class="com.opensymphony.xwork2.validator.validators.IntRangeFieldValidator"/>
<validator name="long" class="com.opensymphony.xwork2.validator.validators.LongRangeFieldValidator"/>
<validator name="short" class="com.opensymphony.xwork2.validator.validators.ShortRangeFieldValidator"/>
<validator name="double" class="com.opensymphony.xwork2.validator.validators.DoubleRangeFieldValidator"/>
<validator name="date" class="com.opensymphony.xwork2.validator.validators.DateRangeFieldValidator"/>
<validator name="expression" class="com.opensymphony.xwork2.validator.validators.ExpressionValidator"/>
<validator name="fieldexpression" class="com.opensymphony.xwork2.validator.validators.FieldExpressionValidator"/>
<validator name="email" class="com.opensymphony.xwork2.validator.validators.EmailValidator"/>
<validator name="url" class="com.opensymphony.xwork2.validator.validators.URLValidator"/>
<validator name="visitor" class="com.opensymphony.xwork2.validator.validators.VisitorFieldValidator"/>
<validator name="conversion" class="com.opensymphony.xwork2.validator.validators.ConversionErrorFieldValidator"/>
<validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>
<validator name="regex" class="com.opensymphony.xwork2.validator.validators.RegexFieldValidator"/>
<validator name="conditionalvisitor" class="com.opensymphony.xwork2.validator.validators.ConditionalVisitorFieldValidator"/>

4.<field-validator>的子元素
<message>错误信息</message>
如果需要国际化,可以为message指定key属性,key的值为属性文件中的key。
5.<field-validator>的子元素
<param name="">值</param>
用于指定校验器中的参数
关于配置校验中的校验器:
  • required (必填校验器,要求被校验的属性值不能为null)
  • requiredstring (必填字符串校验器,要求被校验的属性值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
  • stringlength (字符串长度校验器,要求被校验的属性值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
  • regex (正则表达式校验器,检查被校验的属性值是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
  • int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
  • double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
  • fieldexpression (字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
  • email(邮件地址校验器,要求如果被校验的属性值非空,则必须是合法的邮件地址)
  • url(网址校验器,要求如果被校验的属性值非空,则必须是合法的url地址)
  • date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
注意:关于param的name属性 要到指定校验器对应的类中找到属性的setter方法setXxx 其xxx就是要设置的name属性

问题配置校验,怎样处理在同一个action中存在多个请求处理方法校验问题?
只需要将校验xml文件名称修改即可:
Action简单类名-valication.xml 现在要对action类西红某一个方法校验,
就改为:Action简单类名-action名称-validation.xml
将上面手动配置校验的代码进行修改。
屏蔽UserAction中的所有校验的方法
在同一包下新建UserAction-validation.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<!-- 对username属性进行校验 -->
<field name="username">
<!-- 指定username不能为空 -->
<field-validator type="requiredstring">
<!-- 错误信息 -->
<message>用户名不能为空---</message>
</field-validator>
<!-- 长度校验,规定用户名必须在6-10位之间 -->
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">6</param>
<message>用户名必须在${minLength}-${maxLength}位之间</message>
</field-validator>
</field>
<!-- 对age进行校验,规定年龄必须在10-40之间 -->
<field name="age">
<field-validator type="int">
<param name="min">10</param>
<param name="max">40</param>
<message>年龄必须在${min}--${max}之间</message>
</field-validator>
</field>
<!-- 对birthday进行校验 -->
<field name="birthday">
<field-validator type="date">
<param name="min">1974-01-01</param>
<param name="max">2004-12-31</param>
<message>生日必须在${min}--${max}年之间</message>
</field-validator>
</field>
<!-- 校验邮箱 -->
<field name="email">
<field-validator type="email">
<message>邮箱格式不正确</message>
</field-validator>
</field>
<!-- url校验 -->
<field name="url">
<field-validator type="url">
<message>url不合法,应类似于http://www.baidu.com</message>
</field-validator>
</field>
<!-- 使用正则 -->
<field name="telphone">
<field-validator type="regex">
<param name="regexExpression"><![CDATA[^135[0-9]{8}$]]></param>
<message>电话号码必须是135xxxxxxxx</message>
</field-validator>
</field>
<field name="repassword">
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[(password==repassword)]]></param>
<message>两次密码输入不一致</message>
</field-validator>
</field>
</validators>	
调试结果如下:

登录/login,会有同样的校验
如果要只对regist的action方法进行校验,需要做如下修改
UserAction-validation.xml修改为UserAction-regist-validation.xml即可
修改后调试如下:



发现login方法没有做校验,而regist方法做了校验
当然 也可以自定义校验器,不过很少自定义校验器

基于XML校验的一些特点:

当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:
1。AconClassName-validation.xml
2。ActionClassName-ActionName-validation.xml
系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于处理方法的校验。如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的校验规则。

当action继承了另一个action,父类action的校验文件会先被搜索到。假设UserAction继承BaseAction, UserAction在struts.xml的配置如下:
<action name="user" class="cn.itcast.action.UserAction" method="{1}">
.....
</action>
访问上面名为user的action,系统先搜索到BaseAction-validation.xml, BaseAction-user-validation.xml,接着搜索到UserAction-validation.xml, UserAction-user-validation.xml。校验规则是这四个文件的总和。

编写校验文件时,不能出现提示(帮助信息)解决方案:
在编写ActionClassName-validation.xml校验文件时,如果出现不了帮助信息,可以按下面方式解决:
windwos->preferences->myeclipse->files and editors->xml->xmlcatalog
点“add”,在出现的窗口中的location中选“File system”,然后在xwork-2.1.2解压目录的srcjava目录中选择xwork-validator-1.0.3.dtd,回到设置窗口的时候不要急着关闭窗口,应把窗口中的Key Type改为URI 。Key改为http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd

约定访问提供Struts2的零配置

从struts2.1开始,struts2 引入了Convention插件来支持零配置
使用约定无需struts.xml或者Annotation配置
需要 struts2-convention-plugin-2.3.7.jar 、asm-*.jar(三个)
插件会自动搜索action、actions、struts、struts2包下所有Java类
所有实现了com.opensymphony.xwork2.Action的Java类
所有类名以Action结尾的Java类
下面类名都符合Convention插件
cn.itcast.struts2.HelloAction
cn.itcast.actions.books.BookSearchAction
cn.itcast.struts.user.UserAction
cn.itcast.estore.action.test.LoginAction

struts2-convention-plugin-2.3.7.jar 中struts-plugin.xml重要常量
<constant name="struts.convention.package.locators" value="action,actions,struts,struts2"/> 默认扫描包
<constant name="struts.convention.exclude.packages" value="org.apache.struts.*,org.apache.struts2.*,org.springframework.web.struts.*,org.springframework.web.struts2.*,org.hibernate.*"/> 不扫描
<constant name="struts.convention.action.suffix" value="Action"/> 默认扫描以Action结尾的类
<constant name="struts.convention.result.path" value="/WEB-INF/content/"/> 结果result页面存放位置
<constant name="struts.convention.classes.reload" value="false" /> Action类文件重新自动加载

如果Action类名包含Action后缀,将Action后缀去掉
将Action类名的驼峰写法,转成中划线写法
例如:
cn.itcast.struts2.HelloAction 映射到 /hello.action
cn.itcast.actions.books.BookSearchAction 映射到 /books/book-search.action
cn.itcast.struts.user.UserAction 映射到 /user/user.action
cn.itcast.estore.action.test.LoginAction 映射到 /test/login.action

默认情况下,Convention总会到Web应用的WEB-INF/content路径下定位结果资源
<constant name="struts.convention.result.path" value="/WEB-INF/content/"/>
约定: actionName + resultCode + suffix
例如:
访问cn.itcast.struts.user.UserAction返回success
Convention优先使用 WEB-INF/content/user/user-success.jsp
如果user-success.jsp不存在,会使用user-success.html
如果user-success.html不存在,会使用user.jsp


最后

以上就是朴实小刺猬为你收集整理的JAVAWEB开发之Struts2详解(二)——Action接受请求参数、类型转换器、使用Struts2的输入校验、以及遵守约定规则实现Struts2的零配置...的全部内容,希望文章能够帮你解决JAVAWEB开发之Struts2详解(二)——Action接受请求参数、类型转换器、使用Struts2的输入校验、以及遵守约定规则实现Struts2的零配置...所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部