struts2的一个重要的使用就是参数的传递,既jsp页面的参数值怎么样传递到action中进行业务的处理。
一,Action访问Servlet API
方式一: 在Action 中解耦合方式 间接访问 Servlet API ——— 使用 ActionContext 对象
在struts2 中 Action API 已经与 Servlet API 解耦合 (没有依赖关系 )
* Servlet API 常见操作 : 表单提交请求参数获取,向request、session、application三个范围存取数据
actionContext = ActionContext.getContext();
1)actionContext.getParameters(); 获得所有请求参数Map集合 相当request.getParameter
Map();
2) actionContext.put(“company”, “凯舟教育”); / actionContext.get(“company”) 对request范围存取数据
3) actionContext.getSession(); 获得session数据Map,对Session范围存取数据不是servletApi
4) actionContext.getApplication(); 获得ServletContext数据Map,对应用访问存取数据
方式二、 使用接口注入的方式,操作Servlet API (耦合)
ServletContextAware : 注入ServletContext对象
ServletRequestAware :注入 request对象
ServletResponseAware : 注入response对象
- 程序要使用哪个Servlet的对象,实现对应接口
方式三、 在Action中直接通过 ServletActionContext 获得Servlet API
ServletActionContext.getRequest() : 获得request对象 (session)
ServletActionContext.getResponse() : 获得response 对象
ServletActionContext.getServletContext() : 获得ServletContext对象
* 静态方法没有线程问题,ThreadLocal
二 .Action处理请求参数
struts2 和 MVC 定义关系
StrutsPrepareAndExecuteFilter : 控制器
JSP : 视图
Action : 可以作为模型,也可以是控制器
struts2 Action 接受请求参数 :属性驱动 和 模型驱动
【1.Action处理请求参数三种方式】
第一种 :Action 本身作为model对象,通过成员setter封装 (属性驱动 )
页面:
用户名 <input type="text" name="username" /> <br/>
Action :
public class RegistAction1 extends ActionSupport {
private String username;
public void setUsername(String username) {
this.username = username;
}
}
问题一: Action封装数据,会不会有线程问题 ?
答:struts2 Action 是多实例 ,为了在Action封装数据 (struts1 Action 是单例的 )
问题二: 在使用第一种数据封装方式,数据封装到Action属性中,不可能将Action对象传递给 业务层
答:需要再定义单独JavaBean ,将Action属性封装到 JavaBean
第二种 :创建独立model对象,页面通过ognl表达式封装 (属性驱动)
页面:
用户名:<input type="text" name="user.username" /> <br/> ----- 基于OGNL表达式的写法
Action:
public class RegistAction2 extends ActionSupport {
private User user;
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
}
问题: 谁来完成的参数封装
答:通过下面的拦截器
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
第三种 :使用ModelDriven接口,对请求数据进行封装 (模型驱动 ) —– 主流
页面:
用户名 <input type="text" name="username" /> <br/>
Action :
public class RegistAction3 extends ActionSupport implements ModelDriven<User> {
private User user = new User(); // 必须手动实例化
public User getModel() {
return user;
}
}
优点: 解决了属性驱动存在的问题
缺点: 一次只能封装一个model对象.
另外 struts2 有很多围绕模型驱动的特性
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
为模型驱动提供了更多特性
对比第二种、第三种 : 第三种只能在Action中指定一个model对象,第二种可以在Action中定义多个model对象
<input type="text" name="user.username" />
<input type="text" name="product.info" />
三,封装数据到Collection和Map
1) 封装数据到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 :
public class UserAction extends ActionSupport {
private List<User> users;
public List<User> getProducts() {
return users;
}
public void setProducts(List<User> users) {
this.users= users;
}
}
2) 封装数据到Map 对象
页面:
产品名称 <input type="text" name="map['one'].name" /><br/> ======= one是map的键值
Action :
public class ProductAction2 extends ActionSupport {
private Map<String, Product> map;
public Map<String, Product> getMap() {
return map;
}
public void setMap(Map<String, Product> map) {
this.map = map;
}
}
四, 请求参数的转换
1、 struts2 内部提供大量类型转换器,用来完成数据类型转换问题
boolean 和 Boolean
char和 Character
int 和 Integer
long 和 Long
float 和 Float
double 和 Double
Date 可以接收 yyyy-MM-dd格式字符串
数组 可以将多个同名参数,转换到数组中
集合 支持将数据保存到 List 或者 Map 集合
案例: 输入合法年龄和生日可以自动转换
当输入abc 转换为 int类型 age时
Caused by: java.lang.NoSuchMethodException: com.kz.struts2.demo3.CustomerAction.setAge([Ljava.lang.String;
分析: 输入20 ,转换 int类型20 --- setAge(int)
输入abc,转换int 出错 ---- setAge(String) ----- 报错方法不存在异常
五,自定义类型转换器
1) 自定义类型转换器
第一种 实现TypeConverter接口
convertValue(java.util.Map<java.lang.String,java.lang.Object> context,
java.lang.Object target,
java.lang.String propertyName,
java.lang.Class toType)
第二种 继承 DefaultTypeConverter
convertValue(java.util.Map<java.lang.String,
java.lang.Object> context,
java.lang.Object value,
java.lang.Class toType)
第三种 继承 StrutsTypeConverter
convertFromString(java.util.Map context,
java.lang.String[] values,
java.lang.Class toClass) --- 请求封装
convertToString(java.util.Map context, java.lang.Object o) --- 数据回显
类型转换器 一直都是双向转换
页面提交请求参数,封装到model — 需要转换
model数据 需要在页面 回显 —- 需要转换
2) 以 1990/10/10 为例,自定义日期转换器,完成转换
public Object convertValue(Map<String, Object> context, Object value,
Class toType) {
// 根据toType判断 是请求封装 还是 数据回显
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
if (toType == Date.class) {
// 请求参数封装 (value是字符串)
String[] params = (String[]) value;
String strVal = params[0]; // 转换为 日期类型
try {
return dateFormat.parse(strVal);
} catch (ParseException e) {
e.printStackTrace();
}
} else {
// 回显(value是 Date)
Date date = (Date) value;
return dateFormat.format(date);
}
return null;
}
3) 注册类型转换器
局部注册 : 只对当前Action有效或者model有效(针对属性)
全局注册 : 针对所有Action的日期类型有效 (针对类型 )
具体方法
1.局部–针对于action
配置文件所在位置以及名称: 在Action类所在包 创建Action类名-conversion.properties ,
配置文件书写: 格式 : 属性名称=类型转换器的全类名
2.局部–针对于model
配置文件所在位置以及名称: 在model类所在包 创建model类名-conversion.properties ,
配置文件书写: 格式 : 属性名称=类型转换器的全类名
3.全局
配置文件所在位置以及名称:在src下创建一个xwork-conversion.properties
六,类型转换错误处理
Struts2中的struts-default.xml文件中的defaultStack拦截器栈中18个拦截器中的
通过分析拦截器作用,得知当类型转换出错时,自动跳转input视图 ,在input视图页面中 <s:fieldError/>
显示错误信息
在Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 : invalid.fieldvalue.属性名= 错误信息
注意:
1,如果是自定义类型转换器,出现类型转换问题,要跳转到input视图,在类型转换器中, 必须抛出异常才可以。throw new RuntimeException();
2,action必须继承之ActionSupport (调用里面的addFieldError())
七,请求参数的校验
【1.手工代码校验请求参数】
步骤一: 封装数据
步骤二: 实现 校验Action ,必须继承ActionSupport 类 (查看ActionSupport源码 继承 了validatable validateware接口 具有了校验功能!)
步骤三: 覆盖validate方法,完成对Action的业务方法 数据校验
通过代码逻辑判断参数是否有效,如果参数非法 , this.addFieldError (ActionSupport提供)workflow拦截器 跳转回 input页面
步骤四: 在jsp中 通过 <s:fieldError/>
显示错误信息
validate方法会对Action中所有业务方法进行校验,如果只想校验某一个方法 : validate方法名() ,例如:请求处理方法叫 regist() 校验 的方法名 validateRegist().
原理:
【2.Xml配置方式数据校验】
XML配置方式 数据校验 (企业主流校验)
代码校验 不适用于 大型项目, 流程数据复杂时,开发量和维护量 都会很大
xml配置校验原理 : 将很多校验规则代码已经写好,只需要在xml中定义数据所使用校验规则就可以了
步骤一 :编写jsp
步骤二 :编写Action 必须继承ActionSupport 或者 实现 Validateable 接口
步骤三 :封装请求参数
* 使用xml校验 必须提供get方法
步骤四 :编写校验规则xml文件
在Action所在包 编写 Action类名-validation.xml 对Action所有业务方法进行校验 引入DTD
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">
内置校验器定义文件
xwork-core-2.3.7.jar 中 /com/opensymphony/xwork2/validator/validators/default.xml
书写:
1.根元素
<validators>
2.子元素
<field name="属性名称"></field>
@kate这里的属性名称 由谁决定?
如果属性驱动:jsp页面的name值 例如:user.uname
jsp中:<input name=”user.uname”/>
action中:private Users user;
如果模型驱动:jsp页面的name值 例如:uname
jsp中:<input name=”uname”/>
action中:private Users user;
3.<field>
子元素
<field-validator type="校验器">
这个是指定校验器
问题:校验器有哪些? 看下面
4.子元素
<message>错误信息</message>
5.<field-validator>
子元素
<param name="">值</param>
用于指定校验器中的参数.
步骤五 :获取校验错误信息
<s:fielderror/>
内建校验器—-查看校验器类的源码 属性就是我们要配的参数
required (必填校验器,要求被校验的属性值不能为null)
requiredstring (必填字符串校验器,要求被校验的属性值不能为null,并且长度大于0,默认情况下会对字符串去前后空格 也可以配置<param name=”trim”>false</param>)
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指定最大值)
案例
required 必填校验器
<field-validator type="required">
<message>性别不能为空!</message>
</field-validator>
requiredstring 必填字符串校验器
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空!</message>
</field-validator>
stringlength:字符串长度校验器
<field-validator type="stringlength">
<param name="maxLength">10</param>
<param name="minLength">2</param>
<param name="trim">true</param>
<message><![CDATA[产品名称应在2-10个字符之间]]></message>
</field-validator>
NOTE:要配合非空校验器一起使用
int:整数校验器
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年龄必须在1-150之间</message>
</field-validator>
date: 日期校验器
<field-validator type="date">
<param name="min">1900-01-01</param>
<param name="max">2050-02-21</param>
<message>生日必须在${min}到${max}之间</message>
</field-validator>
url: 网络路径校验器
<field-validator type="url">
<message>凯舟教育的主页地址必须是一个有效网址</message>
</field-validator>
email:邮件地址校验器
<field-validator type="email">
<message>电子邮件地址无效</message>
</field-validator>
regex:正则表达式校验器
<field-validator type="regex">
<param name="regexExpression"><![CDATA[^13\d{9}$]]></param>
<message>手机号格式不正确!</message>
</field-validator>
fieldexpression : 字段表达式校验
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[(password==repassword)]]></param>
<message>两次密码输入不一致</message>
</field-validator>
如何对指定的方法校验 ?
格式 Action类名-ActionName(元素name属性)-validation.xml
例如 : 校验AddCustomerAction中execute方法 配置 <action name="addcustomer" .../>
校验文件名字:
AddCusotmerAction-addcustomer-validation.xml
<action name="regist" class="com.kz.action.UserAction" method="regist">
<result name="input">/regist.jsp</result>
</action>
配置UserAction-regist.validation.xml 只会校验 regist方法
<action name="login" class="com.kz.action.UserAction" method="login">
<result name="input">/regist.jsp</result>
</action>
配置UserAction-login.validation.xml 只会校验 login方法
【3 .自定义校验规则】
步骤一: 自定义校验器 必须实现 Validator 接口
通常自定义校验器 继承 ValidatorSupport 和 FieldValidatorSupport
* ValidatorSupport 针对不是一个输入字段 (两个密码一致)
* FieldValidatorSupport 针对是一个输入字段 (用户名非空)
步骤二: 注册校验器
在工程的src下新建validators.xml文件
引入 xwork-core-2.3.7.jar 中 xwork-validator-config-1.0.dtd
步骤三 :使用校验器
在Action所有包 创建Action类名-validation.xml
实际开发中很少用到自定义校验器 因为有正则表达式可以做任何校验