Struts2第二天
Struts2第二天
昨天:
1、Action的编写方式:实现Action接口、继承ActionSupport、自定义pojo作为action
2、action调用方法:默认的execute、method属性调用、动态方法调用、通配符的使用(最常用的)
3、action访问servlet API:解耦方式、接口注入、静态方法调用
4、结果集的使用:局部和全局结果集、dispatcher、redirect、redirectAction、stream
课程内容:(请求)
-
struts2的请求参数的接收机制(属性驱动、模型驱动)
-
参数的类型转换(自定义类型转换)
-
请求参数的合法性校验(表单参数的合法性校验)(服务器端校验-struts2的校验、自定义校验规则-了解)
-
国际化信息机制(概念、应用:校验信息的国际化、页面和程序的国际化)
课程目标:
-
请求参数的接收和使用
-
自定义类型转换(了解)
-
各类校验的使用
-
国际化信息的使用
-
请求参数的接收机制
Struts2提供两大类(属性驱动、模型驱动)、三种数据封装的方式:
-
属性驱动方式:
-
Action本身作为model对象,通过成员变量的setter方法进行封装。
-
Action中创建独立的model对象和提供对应的setter、getter方法,页面通过ognl表达式进行封装。
-
-
模型驱动方式:
-
使用ModelDriven接口(模型驱动),对请求的数据进行封装。
-
-
属性驱动
-
Action作为model,通过成员变量属性和setter方法接收参数进行封装
-
步骤一:编写一个jsp页面用来提交数据
创建一个a_param文件夹,在该文件夹下创建一个param.jsp页面
内容如下:
<h1>属性驱动1:将action作为model对象,通过属性获取参数并封装</h1>
<form action="${pageContext.request.contextPath }/param1" method="post">
用户名:<input type="text" name="username"/>
密码:<input type="password" name="password"/>
<input type="submit" value="注册">
</form>
步骤二:编写Action获取数据
创建Param1Action
内容如下:
//把action作为一个model对象,通过成员属性来获取数据
//struts底层会通过反射机制获取属性,通过属性设置setter方法,将客户端提交的数据赋值给setter方法;
public class Param1Action extends ActionSupport{
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String execute() throws Exception {
System.out.println("username:"+username);
System.out.println("password:"+password);
return NONE;
}
}
步骤三:配置struts.xml
内容如下:
<struts>
<!-- 配置常量 -->
<constant name="struts.devMode" value="true"></constant>
<package name="default" extends="struts-default" namespace="/">
<!-- 属性驱动1:actoin作为model对象,通过属性的setter方法进行参数的封装 -->
<action name="param1" class="cn.itcast.struts.a_param.Param1Action"/>
</package>
</struts>
步骤四:测试
发送请求
后台打印
分析:
原理:是由struts2的默认栈中有一个默认的拦截器来完成参数封装的
原理分析:Params拦截器,先获取了所有参数(request.getParameters()),然后它就通过反射的机制,到action中寻找有没有set+参数名的方法(set+Username),如果有,则调用,并把参数的值传入进去。
两个注意点:
1.
缺点: Action 往往还需要,通过代码手动将数据封装到一个新的model对象,传递给业务层使用。
2.
问题: Servlet多线程有没有问题? Struts2 Action封装数据,是否存在线程问题 ?
Servlet的开发 ,单实例,不编写成员变量,就没有线程问题
Action 是多实例,每次请求,创建单独对象。
后台:
-
Action中创建独立的model对象和setter、getter方法,页面通过ognl表达式进行封装。
提示:这种类似于第一种,也是属性驱动的一种。
步骤一:编写action获取数据
//属性驱动方法2
//通过一个独立的model对象来回去参数并封装
//分析:先获取了user对象,然后通过页面的ognl表达式提交参数,然后通过user对象的getter和setter方法获取user对象,之后通过对象中的
//属性的setter方法,将参数传递过来
public class Param2Action extends ActionSupport{
private User user;
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
@Override
public String execute() throws Exception {
System.out.println(user.getUsername());
System.out.println(user.getPassword());
return NONE;
}
}
步骤二:配置struts.xml
步骤三:修改页面
<h1>属性驱动方法2:通过独立的model对象获取参数 </h1>
<form action="${pageContext.request.contextPath }/param2"
method="post">
用户名:<input type="text" name="user.username" />
密码:<input type="password" name="user.password" />
<input type="submit" value="提交" />
</form>
步骤四:测试
分析:
运行流程:
struts会自动先获取到参数名为"user.password"的值,struts2会根据.来截取字符串(获得user和password),再通过反射机制:通过getUser ()---->获取user对象,然后判断user是否为null如果为null就new User(),然后再通过user对象的setter方法:setUser(User user)将new的User对象赋值给成员变量,最后在调用User中的user.setPassword(User user)进行参数传递和封装,接下来开始封装第二个参数,发现再次getUser()获取到的user对象如果不为null就直接调用User中的下一个属性的setter方法,如:user.setUsername("zhangsan")进行参数传递和封装
阶段小结:第一种和第二种封装方式类似,都是使用的是Action的属性来封装数据。因此,这两种都统称为 属性驱动封装数据
提示:第二种方法也有缺点,就是需要使用对象的方式来封装属性,代码有点多。
-
模型驱动
-
使用ModelDriven接口(模型驱动),对请求的数据进行封装。
-
步骤一:jsp页面:
步骤二:Action: 实现 ModelDriven接口 ,实现getModel方法
public class Param3Action 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 NONE;
}
}
步骤三:struts.xml配置
步骤四:测试
后台打印:
分析
底层依赖该拦截器:
该拦截器会判断是否实现了ModelDriven接口
如果实现了,则自动调用getModel拿到你要封装的对象。然后,它会通过model中的setter方法自动将参数封装到该对象中的同名成员变量中。
源码分析:
注意:
模型驱动优先于属性驱动来封装数据。
注意:参数只能封装一次
如果即用了模型驱动又用了属性驱动,那么参数会被模型驱动获取,属性驱动就拿不到了。
原因:拦截器顺序问题
只有在modelDriven过滤器无法封装数据的情况下,才会执行属性驱动封装。例如:页面又提交了model对象中没有的一个属性—age,这个时候可以用属性驱动来获取age
【示例】
步骤一:修改页面
添加一个年龄的input标签
步骤二:修改action
添加一个age属性
步骤三:测试
username和password没有获取到数据,因为已经被modelDriven过滤器给封装掉了,而没有封装的age属性就能拿到。
小结:
更喜欢用模型驱动,其实三种方式都可以,看个人习惯,也会混着用,注意优先级的问题。
-
请求参数的类型转换机制
-
内置的类型转换器
-
Struts2内置了常见数据类型多种转换器,如下:
-
boolean 和 Boolean
-
char和 Character
-
int 和 Integer
-
long 和 Long
-
float 和 Float
-
double 和 Double
-
Date 可以接收 yyyy-MM-dd格式字符串
-
数组 可以将多个同名参数,转换到数组中
-
集合 支持将数据保存到 List 或者 Map 集合
-
注册案例
步骤一:提供注册页面
创建一个注册页面:
<!-- 错误回显 -->
<s:fielderror/>
<form action="${pageContext.request.contextPath }/register" method="post">
用户名:<input type="text" name="username"/><br/>
年龄:<input type="text" name="age"/><br/>
生日:<input type="text" name="birth"/><br/>
爱好:<input type="checkbox" name="hobby" value="唱歌"/>唱歌
<input type="checkbox" name="hobby" value="游泳"/>游泳
<input type="checkbox" name="hobby" value="跳舞"/>跳舞
<br/>
<input type="submit" value="提交"/>
</form>
步骤二:编写action
public class RegistAction extends ActionSupport{
private String username;
private int age;
private Date birth;
private String[] hobby;
public void setUsername(String username) {
this.username = username;
}
public void setAge(int age) {
this.age = age;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "RegistAction [username=" + username + ", age=" + age
+ ", birth=" + birth + ", hobby=" + Arrays.toString(hobby)
+ "]";
}
@Override
public String execute() throws Exception {
System.out.println(this);
return NONE;
}
}
步骤三:测试
发送请求:
后台打印:
步骤四:测试提交内置转换不支持的格式
步骤五:测试提交
报错原因:
分析类型转换错误跳转input视图的原因:
步骤六:配置input结果集视图
如果类型转换错误,转发到注册页面重新注册。
<!-- 类型转换 -->
<action name="register" class="cn.itcast.struts.b_conversion.RegisterAction">
<result name="input">/b_conversion/register.jsp</result>
</action>
步骤七:修改register.jsp
页面天struts标签
配置错误标签进行错误信息显示
步骤八:再次测试注册
注册失败,显示错误信息。原因是内置的日期转换器不支持yyyy/MM/dd格式的类型转换
步骤九:显示中文信息
创建一个action所在包下创建一个Action类名.properties的文件:
RegisterAction.properties内容如下:
invalid.fieldvalue.birth为显示错误信息的名称
value为需要显示的错误信息
步骤十:再次测试提交数据
如果内置类型转换器不满足转换要求,可以自定义一个类型转换器
-
参数类型的转换机制
Struts2提供常规类型转换器,可以常用数据类型的转换,但如果目标类型是一个特殊类型,则需要自定义转换器
所有类型转换器必须实现TypeConverter接口
用户需要对特殊数据进行转换,需自定义转换器,就必须实现ognl.TypeConverter接口,可以采用的编写方式:
-
编写类 实现 TypeConverter 接口
-
编写类 继承 DefaultTypeConverter 类
-
编写类 继承 StrutsTypeConverter 类
Struts2 内置转换器:
-
自定义参数类型转换(了解)
分析:用户使用的日期如果是 年/月/日 , struts2内置转换器就无法进行转换,因为期只支持 年-月-日的格式
解决方案:自定义一个类型转换器
步骤一:自定义一个类型转换器,继承DefaultTypeConverter并且复写ConvertValue方法
创建一个自定义类型转换器:
//自定义类型转换器
public class MyDateConversion extends DefaultTypeConverter {
// value:你需要转换的对象
// toType:要转换成的类型
@Override
public Object convertValue(Object value, Class toType) {
// 页面--->服务器:String--->Date
// 服务器--->页面:Date--->String
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
// 注意Date.class的包不要引错:java.util.Date 不是java.sql.Date
if (toType == Date.class) {
try {
return dateFormat.parse(((String[]) value)[0].toString());
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException("日期类型转换错误");
}
} else {
return dateFormat.format(value);
}
}
}
步骤二:注册和使用自定义类型转换器
注册和使用自定义类型转换器(局部):当前action有效
在自定义类型转换器的当前包下创建一个RegistAction-conversion.properties文件:命名规则 类名-conversion.properties
将需要校验的字段和自定义类型转换器关联
注册和使用自定义类型转换器(全局):全部action有效
在src下创建一个xwork-conversion.properties文件
将自定义类型转换器和日期格式关联,当使用日期时都自动使用自定义的类型转换器进行转换
步骤三:分别测试局部和全局:效果无误
-
请求参数的合法性校验机制
-
表单校验的方式
-
请求参数的输入校验途径一般分两种:
-
客户端校验 :通过JavaScript 完成 (jquery validation插件),目的:过滤正常用户的误操作。
-
服务器校验 :通过java代码完成 ,目的:整个应用阻止非法数据的最后防线
两者校验的特点:客户端校验,更加友好, 用户可以第一时间知道数据输入有错误(缺点:安全性差), 服务器校验 更加安全校验机制 (考虑系统健壮性 ,服务器校验必须要做 )
我们这里研究的是:在请求数据封装之后, 在struts2 实现请求数据合法性校验。
Struts2支持校验方式
-
代码中手动数据校验:全局和局部
-
Xml配置规则进行校验(全局和局部)
-
代码手动校验(全局校验)
这种方式是在Action中通过编写代码进行数据校验。
由于需要编写代码,适用于小型项目或者表单数据不多的情况,有代码耦合的问题。
【示例】
步骤一:编写登录页面
新建一个登录页面:
内容如下:
<h1>手动代码校验</h1>
<form action="${pageContext.request.contextPath }/codeValidation"
method="post">
用户名:<input type="text" name="username" /><br>
密码:<input type="password" name="password" /> <br>
<input type="submit" value="登录" />
</form>
步骤二:编写Action进行
Aciton必须继承ActionSupport,因为ActionSupport实现了Validateable接口,该接口中有一个校验方法-validate,需要复写validate()方法,该方法会在其他任何方法之前执行。
编写全局校验方法
-
全局校验 (对当前Action的所有方法进行校验 )
在Action 提供 validate() 方法 --- 对Action中所有业务方法进行校验
public class CodeAction extends ActionSupport implements ModelDriven<User>{
private User user=new User();
public User getModel() {
return user;
}
//全局校验
@Override
public void validate() {
if (StringUtils.isBlank(user.getUsername())) {
this.addFieldError("username", "用户名不能为空");
}
if (StringUtils.isBlank(user.getPassword())) {
this.addFieldError("password", "密码不能为空");
}
}
@Override
public String execute() throws Exception {
return NONE;
}
}
思考,校验失败后,页面是怎么跳转的?
校验失败后,应该阻止执行其他的方法,那么应该怎么阻止呢?
通过addFieldError()方法将错误信息存放到fieldError中。然后拦截器-xworkflow会判断fieldError是否为空,如果不为空就跳转到input视图,因此在struts.xml中我们还必须配置input视图。
步骤三:struts.xml
配置input视图
<!-- 手动代码校验:全局 -->
<action name="codeValidation" class="cn.itcast.struts.c_validation.CodeValidationAction">
<result name="input">/c_validation/validation.jsp</result>
</action>
步骤四:修改jsp,配置提示信息
在jsp页面引入标签
配置错误信息提示标签:
<body>
<s:fielderror/>
<h1>手动代码校验</h1>
<form action="${pageContext.request.contextPath }/codeValidation"
method="post">
用户名:<input type="text" name="username" /><s:fielderror fieldName="username"/><br>
密码:<input type="password" name="password" /><s:fielderror fieldName="password"/> <br>
<input type="submit" value="登录" />
</form>
</body>
步骤五:测试
执行结果如下:
校验机制原理分析:
-
代码手动校验(局部)
-
局部校验 (校验Action中指定业务方法—校验一个方法 )
-
必须要求实现validateable接口
如果需要给login方法进行校验,那么需要再写一个validteLoging的方法,该方法名称固定为: validateXxx 方法(方法名首字母大写), 这里XXX 是要校验目标方法名 (只会对指定方法校验)
【示例】
步骤一:准备表单数据
<h1>手动代码校验</h1>
<form action="${pageContext.request.contextPath }/codeValidation_login"
method="post">
用户名:<input type="text" name="username" /><s:fielderror fieldName="username"/><br>
密码:<input type="password" name="password" /><s:fielderror fieldName="password"/> <br>
<input type="submit" value="登录" />
</form>
步骤二:Action
添加login方法
步骤三:Struts.xml
注意:测试的时候要走指定的方法,因此,需要在struts.xml中配置指定方法调用
需要配置input视图
<!-- 手动代码校验:局部 -->
<action name="codeValidation_login" class="cn.itcast.struts.c_validation.CodeValidationAction" method="login">
<result>/c_validation/success.jsp</result>
<result name="input">/c_validation/validation.jsp</result>
</action>
步骤四:测试
执行效果如下:
提示:全局校验一直会执行,即使有局部校验。且先走的局部校验。
一定要配置input结果集视图
-
XML配置规则校验
手动代码校验,不适用于 表单元素非常多情况,造成代码量很大,且不容易维护(代码耦合性太强!!!)。企业中一般推荐使用xml文件来配置表单校验。
执行xml配置校验的要求: Action 必须继承ActionSupport类 (为了实现 Validateable接口)。
这里根据校验规则生效的范围分为全局校验和局部校验两种。
注意:使用xml来校验,如果action是通过属性驱动方式一来获取参数的话,属性必须都有setter和getter方法,setter是用来获取参数的,而getter因为xml校验器需要使用getter方法获取到数据之后才能进行校验。
-
全局校验
作用域范围:校验当前Action 所有方法-是针对某一个action中的所有方法。
编写xml的方法:在Action类所在包,创建 Action类名-validation.xml
步骤一:创建表单数据
<h1>xml全局校验</h1>
<form action="${pageContext.request.contextPath }/xmlValidation"
method="post">
用户名:<input type="text" name="username" /><s:fielderror fieldName="username"/><br>
密码:<input type="password" name="password" /><s:fielderror fieldName="password"/> <br>
<input type="submit" value="登录" />
</form>
步骤二:编写Action
创建Action:
内容如下:
注意:必须要有getter和setter方法
setter:获取参数用的
getter:xml校验的时候,struts2底层会通过反射机制从getter方法来获取参数值才能进行校验
//xml校验:需要实现validateable接口
public class XmlValidationAction extends ActionSupport{
private String username;
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String execute() throws Exception {
System.out.println(username+"..."+password);
return super.execute();
}
}
步骤三:创建校验文件
命名规则:Action名称-validation.xml
引入头信息 DTD
(xwork-core包 最下面 xwork-validator-1.0.3.dtd)
配置提示
内容如下:
提示:这些都是struts2内置的校验器,每一种校验器都可以实现一种校验规则方式。只需要记住常用的几个就行。
<validators>
<field name="username">
<field-validator type="requiredstring">
<message>用户名不能为空!(xml全局校验)</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>密码不能为空!(xml全局校验)</message>
</field-validator>
</field>
</validators>
提示:配置校验器,可以参考com.opensymphony.xwork2.validator.validators 下 default.xml
步骤四:Struts.xml:
<!-- xml校验:全部 -->
<action name="xmlValidation" class="cn.itcast.struts.c_validation.XmlValidationAction" >
<result>/c_validation/success.jsp</result>
<result name="input">/c_validation/validation.jsp</result>
</action>
步骤五:测试
执行结果如下:
-
局部校验
作用域范围:校验当前Action的指定的方法。
编写xml的方法:在Action类所在包,创建 Action类名-<action>name属性-validation.xml
步骤一:编写页面
创建一个页面:
注册表单register.jsp(常用校验)
<s:fielderror/>
<h1>xml配置校验--局部</h1>
<s:fielderror/>
<form action="${pageContext.request.contextPath }/xmlValidation_register" method="post">
用户名:<input type="text" name="username"/>(非空,且长度为3-10位)<br/>
密码:<input type="password" name="password"/>(必须,且长度为6-12)<br/>
重复密码:<input type="password" name="repassword"/>(必须和密码一致)<br/>
年龄:<input type="text" name="age"/>(数字,且必须是18-100)<br/>
手机号码:<input type="text" name="mobile"/>(手机号规则,11位数字)<br/>
邮箱:<input type="text" name="email"/>(邮箱格式)<br/>
<input type="submit" value="登录"/>
</form>
步骤二:Action
属性驱动方式一需要提供setter和getter方法
public class RegisterAction extends ActionSupport{
private String username;
private String password;
private String repassword;
private int age;
private String mobile;
private String email;
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 getRepassword() {
return repassword;
}
public void setRepassword(String repassword) {
this.repassword = repassword;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String register(){
System.out.println("用户注册成功!");
return NONE;
}
}
步骤三:编写局部校验器文件
命名规则:类名-<action>中的name值-validation.xml
引入头信息 DTD
(xwork-core包 最下面 xwork-validator-1.0.3.dtd)
配置提示:struts-2.3.15.3\src\xwork-core\src\main\resources\xwork-validator-1.0.3.dtd
内容如下:
<?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>
<field name="username">
<field-validator type="requiredstring">
<message>用户名不能为空....!</message>
</field-validator>
<field-validator type="stringlength">
<param name="maxLength" >10</param>
<param name="minLength" >3</param>
<message>用户名长度必须为3-10位</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>密码不能为空....!</message>
</field-validator>
<field-validator type="stringlength">
<param name="maxLength" >12</param>
<param name="minLength" >6</param>
<message>密码长度必须为6-12位</message>
</field-validator>
</field>
<field name="repassword">
<field-validator type="fieldexpression">
<param name="expression">repassword==password</param>
<message>密码重复错误....!</message>
</field-validator>
</field>
<field name="age">
<field-validator type="int">
<param name="min">18</param>
<param name="max">100</param>
<message>年龄只能在18-100之间</message>
</field-validator>
</field>
<field name="mobile">
<field-validator type="regex">
<param name="regex"><![CDATA[^1[3456789]\d{9}$]]></param>
<message>年龄只能在18-100之间</message>
</field-validator>
</field>
<field name="email">
<field-validator type="email">
<message>邮箱地址不正确</message>
</field-validator>
</field>
</validators>
步骤四:配置struts.xml
<!-- xml校验:局部 -->
<action name="xmlValidation_register" class="cn.itcast.struts.c_validation.RegisterAction" method="register">
<result name="input">/c_validation/register.jsp</result>
</action>
步骤五:测试
执行效果:
提示:
在编写校验的时候,可参考default.xml和源码来配置。
提示:在Action 执行xml 校验时, 如果使用到了属性驱动方式一,就必须要为 变量提供getter方法!!!
-
小结
Xml校验是先走全局校验再走局部校验,正好和手动校验的顺序相反
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。校验规则是这四个文件的总和。
Struts内置校验器:
required (必填校验器,要求field的值不能为null)
requiredstring (必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
stringlength(字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式.expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
fieldexpression(字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
email(邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址)
url(网址校验器,要求如果field的值非空,则必须是合法的url地址)
date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
conversion(转换校验器,指定在类型转换失败时,提示的错误信息)
visitor(用于校验action中的复合属性,它指定一个校验文件用于校验复合属性中的属性)
expression(OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中)
-
自定义校验规则 (课后了解)
-
自定义验证程序必须实现 Validator 接口.
-
若需要字段验证程序, 可以继承 FieldValidatorSupport 类
-
注册验证程序: 在src下创建一个validators.xml 文件,在该文件中进行自定义校验器的注册,拦截器会自动到src下去查找该文件。
-
位于com.opensymphony.xwork2.validator.validators 包下
自定义校验器,实现Validator接口, 一般可以继承FieldValidatorSupport
需求:自定义一个手机号校验的校验器
步骤一:编写页面
创建页面:
内容:
<h1>自定义校验</h1>
<form action="${pageContext.request.contextPath }/register"
method="post">
用户名:<input type="text" name="username" /><br>
密码:<input type="password" name="password" /> <br>
电话:<input type="text" name="mobile"/>
<input type="submit" value="登录" />
</form>
步骤二:编写action
内容如下:必须提供getter和setter方法
public class RegistAction extends ActionSupport {
private String username;
private String password;
private String mobile;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getMobile() {
return mobile;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
@Override
public String execute() throws Exception {
System.out.println(username+"..."+password+"..."+mobile);
return NONE;
}
}
步骤三: 编写自定义 校验器类 继承 FieldValidatorSupport
public class CustomValidate extends FieldValidatorSupport {
/**
* object 表示当前执行的action对象 object
*
*/
public void validate(Object object) throws ValidationException {
// 获取字段的名称手机号
String fieldName = this.getFieldName();
// 获取字段的值
Object fieldValue = this.getFieldValue(fieldName, object);
System.out.println(fieldName + " " + fieldValue);
//判断是否是字符串
String str=(String)fieldValue;
String regex="^1[3458]\\d{9}$";
boolean flag = str.matches(regex);
if (!flag) {
//如果校验失败添加错误信息,使workflow拦截器直接跳转到input结果集
this.addFieldError(fieldName, object);
}
}
}
步骤四: 注册校验器
在src新建 validators.xml
引入DTD xwork core 下面 xwork-validator-config-1.0.dtd
校验规则:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator Config 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-config-1.0.dtd">
<validators>
<validator name="mobile" class="cn.itcast.struts.c_validation.CustomValidation"></validator>
</validators>
步骤五: 使用一个xml校验器
创建xml校验文件:
内容如下:
<validators>
<field name="mobile">
<field-validator type="mobileValidate">
<message>手机号码不正确,请重新输入!</message>
</field-validator>
</field>
</validators>
步骤六:测试
填写数据:
测试结果:
-
国际化信息机制
-
国际化的概念
国际化 (I18N) : 一套机制,拥有多套配置文件, 提供不同国家、地区的语言显示。
-------- 将信息提示文件,配置properties 获取
国际化的概念和用途:
底层对象: ResourceBundle,自动去读取不同国家、语言信息properties文件,该文件的名字是有规范的,一般会有三种方式:
- 自定义名_语言代码_国别代码.properties(baseName_language_country.properties)。
- 自定义名_语言代码.properties(baseName_language.properties)。
-
自定义名.properties(baseName.properties)。
比如:
message_zh_CN.properties:中文(简体,中国)
message_en_US.properties:英语(美国)
message_zh.properties:中文
message_en.properties:英语
message.properties:默认的文件
【提示】语言小写, 国家大写
资源文件寻找顺序:
优先精确匹配—》模糊匹配。: message_zh_CN.properties-> message_zh.properties-> message.properties
依次向下搜索。
当不同的系统语言地区来访问的时候,会自动调用不同的资源文件。
【了解】windows下查看默认语言和区域:
如果没有读取到对应语言和国家的资源文件,会自动去读取默认的配置文件(不带语言和国家),如message.properties
国际化文件的内容的编写:
如果要向properties 写入中文,需要用JDK 内置 native2ascii 命令来转码。如:
另外,若使用myeclipse开发工具,内置properties editor会自动将中文转换 Unicode码
常见的应用: struts2 国际化文件应用在哪 ?
-
错误信息显示 (类型转换错误, 数据合法性校验错误 )-常用
-
程序中主动获取国际化信息,如页面直接获取以及Action代码中获取。
Struts2对国际化的api进行了封装,只需要配置即可使用。下面根据不同的可访问调用的范围,分别进行讲解:
-
全局范围的国际化文件
-
Acton范围的国际化文件
-
Package范围的国际化文件
-
合法性校验获取国际化文件信息
案例采用请求参数合法性校验错误的案例。
-
全局范围的国际化文件
作用范围:全局国际化文件,对所有Action 生效,任何程序都可以访问到(针对某个工程)
配置方法:需要在struts.xml 配置常量 struts.custom.i18n.resources指定信息文件
将全局的国际化信息文件,创建在src目录
步骤一:创建页面
内容如下:
<s:fielderror/>
<!-- 国际化测试:商品添加 -->
<form action="${pageContext.request.contextPath }/product_add" method="post">
商品名称:<input type="text" name="name"/>
商品价格:<input type="text" name="price"/>
<input type="submit" value="添加"/>
</form>
步骤二:编写ProductAction
创建action
内容如下:必须编写setter和getter方法,getter方法是为了校验的时候从获取参数用的
public class ProductAction extends ActionSupport{
private String name;
private double price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String add(){
System.out.println("添加商品成功!商品名称:"+name+"..........商品价格:"+price);
return NONE;
}
}
提示:需要给属性赋予getter和setter方法。
步骤三:添加xml方式的局部校验
局部校验的命名方式:ActionName-<action>name值-validation.xml
内容如下:注意区别,通过标签中key属性来引用message.properties文件中的信息。
<validators>
<field name="name">
<field-validator type="requiredstring">
<message key="Product.name.required"></message>
</field-validator>
</field>
<field name="price">
<field-validator type="double">
<param name="minInclusive">1.00</param>
<param name="maxInclusive">10000.00</param>
<message key="Product.price.length"></message>
</field-validator>
</field>
</validators>
步骤四:创建国际化信息文件
新建国际化信息文件 src下 messages.properties (默认的国际化文件)
内容如下:
步骤五:struts.xml
注意—要使用国际化信息需要先开启国际化信息的常量配置
<!-- 国际化信息机制 -->
<action name="product_*" class="cn.itcast.struts.d_i18n.ProductAction" method="{1}">
<result name="success">/d_i18n/success.jsp</result>
<result name="input">/d_i18n/product.jsp</result>
</action>
步骤六:测试
执行结果如下:
提示:全局的国际化文件,所有的action都能用。
-
测试全局国际化文件读取顺序
资源文件寻找顺序:
优先精确匹配—》模糊匹配。
创建一个更为精确的国际化文件:
message.properties:
messsage_zh.properties:
message_zh_CN.properties;
再次测试:成功
-
Action范围的国际化文件
作用范围:只对当前Action 生效
配置方法:在Action类所在包 创建 Action类名.properties (无需在struts.xml 配置 )
内容:
测试:效果
-
package范围的国际化文件
作用范围:对package 下所有Action 都生效 (包括子包 )
配置方法:在package下面 建立 package.properties (无需在struts.xml )
注意:需要将action范围的properties文件去掉,否则按照就近原则会先在action范围内生效。
内容:
测试:
-
小结
-
三种国际化文件的有效的优先级:Action—》package—》全局
-
如果国际化的信息中的key写错了,显示信息的地方会直接打印key
国际化信息机制的获取顺序:action范围->package.properties范围->message_zh_CN.properties-> message_zh.properties-> message.properties
-
程序中主动获取国际化信息
-
在JSP页面获取
-
国际化的页面上读取:
使用<s:text name="国际化的key"/>
步骤一:页面
product.jsp:
<s:fielderror />
<!-- 国际化测试:商品添加 -->
<form action="${pageContext.request.contextPath }/product_add"
method="post">
<s:text name="ProductName" />
:<input type="text" name="name" />
<s:text name="ProductPrice" />
:<input type="text" name="price" /> <input type="submit" value="添加" />
</form>
步骤二:增加国际化信息
修改国际化文件:
内容如下:
步骤三:struts.xml
开启国际化
步骤四:测试
执行结果如下:
有切换语言:
-
更改了框架的识别的语言的版本。(国际化)
-
更改不同语言的模版
-
自动翻译
-
读取非默认的国际化文件
如果国际化文件的命名并不是以推荐的方式命名的,而页面非要引用的话,可以在页面使用<s:i18n>标签来指定它。
步骤一:新增一个国际化文件
内容如下:
步骤二:修改页面
<form action="${pageContext.request.contextPath }/product_add" method="post">
<s:i18n name="abc">
<s:text name="ProductName"/>
</s:i18n>:<input type="text" name="name"/>
<s:i18n name="abc">
<s:text name="ProductPrice"/>
</s:i18n>:<input type="text" name="price"/>
<input type="submit" value="添加"/>
</form>
步骤三:测试
-
在Action代码获取
步骤一:添加国际化信息
步骤二:修改action
public String add(){
System.out.println(this.getText("Product.addSuccess"));
return NONE;
}
步骤二:测试
提示:
-
通过 getText 可以读取国际化文件
-
读取的顺序:依次读取 Action范围、package范围、全局
-
【国际化占位符的问题】
步骤一:国际化文件
占位符配置
步骤二:页面配置占位符参数
<s:i18n name="abc">
<s:text name="welcome">
<s:param>凤姐</s:param>
<s:param>传智博客</s:param>
</s:text>
</s:i18n>
步骤三:测试
注意:页面取值如果不是从以下默认命名的国际化文件中取的话,就需要用<s:i18n name="xxxxx">来指定国际化文件。
-
【代码中取值示例】
步骤一:在message.properties中编写国际化文件
步骤二:Action配置占位符参数
参数可以使用String[] 、List 传递
步骤三:测试
-
内容整理
-
请求参数的接收机制
-
属性驱动方式1
-
-
通过属性的setter方法获取参数
-
属性驱动方式2
创建一个独立的model对象,然后通过这个对象的setter和getter方法获取参数
如果没有getter方法:只能封装最后一个参数
-
模型驱动
实现ModelDriven,初始化model对象,通过getModel()返回model对象。
注意:如果即使用的属性驱动方式1又使用的模型驱动,此时如果需要获取的参数一致的话,会优先使用模型驱动来封装数据。
只有在模型驱动的model对象中没有该属性的时候,属性驱动才能获取。
-
请求参数的类型转换机制
-
Struts内置的类型转换期
-
Date类型:只能转换yyyy-MM-dd
-
合法性的校验
-
手动代码校验-全局
-
特点:在所有方法执行之前执行。方法名称:validate,需要实现ActionSupport,就是为了继承validateable和ValidationAware
-
手动代码校验-局部
他的执行顺序是在全局校验前面。
命名方式:validateXxx-----validate+首字母大写的方法名称
-
Xml校验全局
全局校验Xml的命名方式:Action的类型-validation.xml,需要引入约束
-
Xml校验局部
局部校验Xml的命名方式:Action的类名-<action>标签中的name的属性值-validation.xml
Xml校验的执行顺序:正好和手动代码校验相反。
-
国际化信息机制
-
国际化信息的使用方式
-
-
开启国际化信息机制的常量
-
创建国际化文件
-
国际化信息机制的种类
action范围的
package.properties:包范围的
message_zh_CN.properties
message_zh.properties
message.properties;
-
程序中获取国际化信息机制
-
jsp页面:<s:text>
-
获取非默认的国际化文件:<s:i18n>包裹<s:text>
-
Action中获取国际化信息:this.getText(国际化文件中的name)
-
占位符的使用:
jspà可在<s:text>标签中设置<s:param>参数
action中设置国际化信息文件中的占位符:this.getText(国际化文件中的name,String数组)
-
重点和小结
-