Struts2第二天

昨天:

1、Action的编写方式:实现Action接口、继承ActionSupport、自定义pojo作为action

2、action调用方法:默认的execute、method属性调用、动态方法调用、通配符的使用(最常用的)

3、action访问servlet API:解耦方式、接口注入、静态方法调用

4、结果集的使用:局部和全局结果集、dispatcher、redirect、redirectAction、stream

课程内容:(请求)

  1. struts2的请求参数的接收机制(属性驱动、模型驱动)
  2. 参数的类型转换(自定义类型转换)
  3. 请求参数的合法性校验(表单参数的合法性校验)(服务器端校验-struts2的校验、自定义校验规则-了解)
  4. 国际化信息机制(概念、应用:校验信息的国际化、页面和程序的国际化)

课程目标:

  1. 请求参数的接收和使用
  2. 自定义类型转换(了解)
  3. 各类校验的使用
  4. 国际化信息的使用
  1. 请求参数的接收机制

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

  • 属性驱动方式:
    • Action本身作为model对象,通过成员变量的setter方法进行封装。
    • Action中创建独立的model对象和提供对应的setter、getter方法,页面通过ognl表达式进行封装。
  • 模型驱动方式:
    • 使用ModelDriven接口(模型驱动),对请求的数据进行封装。
  1. 属性驱动

    1. Action作为model,通过成员变量属性和setter方法接收参数进行封装

步骤一:编写一个jsp页面用来提交数据

创建一个a_param文件夹,在该文件夹下创建一个param.jsp页面

内容如下:

:将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="/">

: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 是多实例,每次请求,创建单独对象。

后台:

  1. Action中创建独立的model对象和setter、getter方法,页面通过ognl表达式进行封装。

提示:这种类似于第一种,也是属性驱动的一种。

步骤一:编写action获取数据

//通过一个独立的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

步骤三:修改页面

:通过独立的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的属性来封装数据。因此,这两种都统称为 属性驱动封装数据

提示:第二种方法也有缺点,就是需要使用对象的方式来封装属性,代码有点多。

  1. 模型驱动

    1. 使用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属性就能拿到。

小结:

更喜欢用模型驱动,其实三种方式都可以,看个人习惯,也会混着用,注意优先级的问题。

  1. 请求参数的类型转换机制

    1. 内置的类型转换器

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

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

步骤一:提供注册页面

创建一个注册页面:

<!-- 错误回显 -->

<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为需要显示的错误信息

步骤十:再次测试提交数据

如果内置类型转换器不满足转换要求,可以自定义一个类型转换器

  1. 参数类型的转换机制

Struts2提供常规类型转换器,可以常用数据类型的转换,但如果目标类型是一个特殊类型,则需要自定义转换器

所有类型转换器必须实现TypeConverter接口

用户需要对特殊数据进行转换,需自定义转换器,就必须实现ognl.TypeConverter接口,可以采用的编写方式:

  • 编写类 实现 TypeConverter 接口
  • 编写类 继承 DefaultTypeConverter 类
  • 编写类 继承 StrutsTypeConverter 类

Struts2 内置转换器:

  1. 自定义参数类型转换(了解)

分析:用户使用的日期如果是 年/月/日 , 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文件

将自定义类型转换器和日期格式关联,当使用日期时都自动使用自定义的类型转换器进行转换

步骤三:分别测试局部和全局:效果无误

  1. 请求参数的合法性校验机制

    1. 表单校验的方式

请求参数的输入校验途径一般分两种:

  • 客户端校验 :通过JavaScript 完成 (jquery validation插件),目的:过滤正常用户的误操作。
  • 服务器校验 :通过java代码完成 ,目的:整个应用阻止非法数据的最后防线

两者校验的特点:客户端校验,更加友好, 用户可以第一时间知道数据输入有错误(缺点:安全性差), 服务器校验 更加安全校验机制 (考虑系统健壮性 ,服务器校验必须要做 )

我们这里研究的是:在请求数据封装之后, 在struts2 实现请求数据合法性校验。

Struts2支持校验方式

  • 代码中手动数据校验:全局和局部
  • Xml配置规则进行校验(全局和局部)
  1. 代码手动校验(全局校验)

这种方式是在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>

步骤五:测试

执行结果如下:

校验机制原理分析

  1. 代码手动校验(局部)

  • 局部校验 (校验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结果集视图

  1. XML配置规则校验

手动代码校验,不适用于 表单元素非常多情况,造成代码量很大,且不容易维护(代码耦合性太强!!!)。企业中一般推荐使用xml文件来配置表单校验。

执行xml配置校验的要求:    Action 必须继承ActionSupport类 (为了实现 Validateable接口)。

这里根据校验规则生效的范围分为全局校验和局部校验两种。

注意:使用xml来校验,如果action是通过属性驱动方式一来获取参数的话,属性必须都有setter和getter方法,setter是用来获取参数的,而getter因为xml校验器需要使用getter方法获取到数据之后才能进行校验。

  1. 全局校验

作用域范围:校验当前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>

步骤五:测试

执行结果如下:

  1. 局部校验

作用域范围:校验当前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/>

位数字)<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方法!!!

  1. 小结

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时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中)

  1. 自定义校验规则 (课后了解)

  • 自定义验证程序必须实现 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>

步骤六:测试

填写数据:

测试结果:

  1. 国际化信息机制

  1. 国际化的概念

国际化 (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范围的国际化文件
  1. 合法性校验获取国际化文件信息

案例采用请求参数合法性校验错误的案例。

  1. 全局范围的国际化文件

作用范围:全局国际化文件,对所有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都能用。

  1. 测试全局国际化文件读取顺序

资源文件寻找顺序:

优先精确匹配—》模糊匹配。

创建一个更为精确的国际化文件:

message.properties:

messsage_zh.properties:

message_zh_CN.properties;

再次测试:成功

  1. Action范围的国际化文件

作用范围:只对当前Action 生效

配置方法:在Action类所在包 创建 Action类名.properties (无需在struts.xml 配置 )

内容:

测试:效果

  1. package范围的国际化文件

作用范围:对package 下所有Action 都生效 (包括子包 )

配置方法:在package下面 建立 package.properties (无需在struts.xml )

注意:需要将action范围的properties文件去掉,否则按照就近原则会先在action范围内生效。

内容:

测试:

  1. 小结

  1. 三种国际化文件的有效的优先级:Action—》package—》全局
  2. 如果国际化的信息中的key写错了,显示信息的地方会直接打印key

国际化信息机制的获取顺序:action范围->package.properties范围->message_zh_CN.properties-> message_zh.properties-> message.properties

  1. 程序中主动获取国际化信息

    1. 在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

开启国际化

步骤四:测试

执行结果如下:

有切换语言:

  • 更改了框架的识别的语言的版本。(国际化)
  • 更改不同语言的模版
  • 自动翻译
  1. 读取非默认的国际化文件

如果国际化文件的命名并不是以推荐的方式命名的,而页面非要引用的话,可以在页面使用<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>

步骤三:测试

  1. 在Action代码获取

步骤一:添加国际化信息

步骤二:修改action

public String add(){

System.out.println(this.getText("Product.addSuccess"));

return NONE;

}

步骤二:测试

提示:

  1. 通过 getText 可以读取国际化文件
  2. 读取的顺序:依次读取 Action范围、package范围、全局
  1. 【国际化占位符的问题】

步骤一:国际化文件

占位符配置

步骤二:页面配置占位符参数

<s:i18n name="abc">

<s:text name="welcome">

<s:param>凤姐</s:param>

<s:param>传智博客</s:param>

</s:text>

</s:i18n>

步骤三:测试

注意:页面取值如果不是从以下默认命名的国际化文件中取的话,就需要用<s:i18n name="xxxxx">来指定国际化文件。

  1. 【代码中取值示例】

步骤一:在message.properties中编写国际化文件

步骤二:Action配置占位符参数

参数可以使用String[] 、List 传递

步骤三:测试

  1. 内容整理

    1. 请求参数的接收机制

      1. 属性驱动方式1

通过属性的setter方法获取参数

  1. 属性驱动方式2

创建一个独立的model对象,然后通过这个对象的setter和getter方法获取参数

如果没有getter方法:只能封装最后一个参数

  1. 模型驱动

实现ModelDriven,初始化model对象,通过getModel()返回model对象。

注意:如果即使用的属性驱动方式1又使用的模型驱动,此时如果需要获取的参数一致的话,会优先使用模型驱动来封装数据。

只有在模型驱动的model对象中没有该属性的时候,属性驱动才能获取。

  1. 请求参数的类型转换机制

    1. Struts内置的类型转换期

Date类型:只能转换yyyy-MM-dd

  1. 合法性的校验

    1. 手动代码校验-全局

特点:在所有方法执行之前执行。方法名称:validate,需要实现ActionSupport,就是为了继承validateable和ValidationAware

  1. 手动代码校验-局部

他的执行顺序是在全局校验前面。

命名方式:validateXxx-----validate+首字母大写的方法名称

  1. Xml校验全局

全局校验Xml的命名方式:Action的类型-validation.xml,需要引入约束

  1. Xml校验局部

局部校验Xml的命名方式:Action的类名-<action>标签中的name的属性值-validation.xml

Xml校验的执行顺序:正好和手动代码校验相反。

  1. 国际化信息机制

    1. 国际化信息的使用方式

  1. 开启国际化信息机制的常量
  2. 创建国际化文件
  1. 国际化信息机制的种类

action范围的

package.properties:包范围的

message_zh_CN.properties

message_zh.properties

message.properties;

  1. 程序中获取国际化信息机制

  1. jsp页面:<s:text>
  2. 获取非默认的国际化文件:<s:i18n>包裹<s:text>
  3. Action中获取国际化信息:this.getText(国际化文件中的name)
  4. 占位符的使用:

    jspà可在<s:text>标签中设置<s:param>参数

    action中设置国际化信息文件中的占位符:this.getText(国际化文件中的name,String数组)

    1. 重点和小结

Struts2第二天的更多相关文章

  1. struts2第二天——数据操作

    先介绍一下大致内容: 大致内容: 结果页面配置 action获取表单提交数据 提供获取表单数据的方式(封装数据) 表单数据封装到集合中 表达式封装和模型驱动封装比较 一.结果页面配置: result标 ...

  2. Struts2 第二讲 -- Struts2的入门

    搭建struts2环境时,我们一般需要做以下几个步骤的工作: 第一步:创建javaweb工程(这个很废话有木有) 第二步:找到开发Struts2应用需要使用到的jar文件.(这个很白痴有没有) 到ht ...

  3. SSH框架之Struts2第二篇

    1.2 知识点 1.2.1 Struts2的Servlet的API的访问 1.2.1.1 方式一 : 通过ActionContext实现 页面: <h1>Servlet的API的访问方式一 ...

  4. Struts2第二篇【开发步骤、执行流程、struts.xml讲解、defalut-struts讲解】

    前言 我们现在学习的是Struts2,其实Struts1和Struts2在技术上是没有很大的关联的.Struts2其实基于Web Work框架的,只不过它的推广没有Struts1好,因此就拿着Stru ...

  5. struts2 第二天

    3.自动装配  零散属性:Action类中两个成员变量的名称和页面上表单元素name属性值保持一致.      规则:约定优于配置.  领域模型:两种配置    public class FirstA ...

  6. SSH开发模式——Struts2(第二小节)

    上一小节已经学会了如何去搭建Struts2的开发环境,该篇博客我们继续深入Struts2,了解Struts2框架的拦截器. 首先对我们在web.xml文件配置的过滤器进行一个源码的分析. 在Strut ...

  7. strits2初始配置总结

    **************************************************************************************************** ...

  8. struts2框架学习之第二天

    day02 下面是在每个Action之前都会执行的拦截器,这段代码来自与struts-default.xml文件. <interceptor-stack name="defaultSt ...

  9. 第二篇——Struts2的Action搜索顺序

    Struts2的Action的搜索顺序: 地址:http://localhost:8080/path1/path2/student.action     1.判断package是否存在,例如:/pat ...

随机推荐

  1. sublime3下载安装及常用插件

    之前与学习前端有关的软件都安装在了实验室电脑上,最近由于要放寒假(也许我寒假回去会学习呢),于是得在笔记本电脑上重新安装一遍.几个软件各种出错,花了一下午才安装好,必须记录下来啊! 这篇文章主要介绍s ...

  2. 安装vsftp流程整理

    昨天装个FTP,发现之前写的一篇操作日志太简陋了,重新整理了下记在这儿 # 安装 VSFTP yum -y install vsftpd # 创建FTP日志文件路径 touch /var/log/vs ...

  3. [z] .net与java建立WebService再互相调用

    http://blog.csdn.net/yenange/article/details/5824967 : .net建立WebService,在Java中调用. 1.在vs中新建web 简单修改一下 ...

  4. [UWP小白日记-7]转换MVA学院的XML字幕为SRT (二)

    瞎扯淡 上个版本,非常蠢用来N多的循环导致非常卡性能烂得不行,这次使用XmlDocument类来读取XML字幕 其实根本不用各种扒XML字幕,好吧我这是学习使用XmlDocument类,嗯就是这个样子 ...

  5. 未能加载文件或程序集"Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad的真正解决办法

    未能加载文件或程序集"Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3 ...

  6. C# 中 重载,重写,隐藏的区别

    重载: 就是写多个同名方法,参数个数不同或类型不同或返回值不同 重写:子类中实现的方法必须加override关键词   普通非抽象父类需要virtual 抽象类里面抽象方法abstract 接口的实现 ...

  7. Linode VPS上架日本东京2号机房,性能速度评测报告(推荐)

    我非常喜欢的海外免备案vps品牌linode日本机房长期缺货,中国用户想买都买不到.不过近日,陆续有国内朋友收到了Linode邀请,Tokyo 2日本东京机房开启内测,很快正式上架销售. 苦等太久的站 ...

  8. Openjudge-NOI题库-和为给定数

    题目描述 Description 给出若干个整数,询问其中是否有一对数的和等于给定的数.  输入输出格式 Input/output 输入格式: 共三行: 第一行是整数n(0 < n <= ...

  9. json字符串和对象的相互转化

    json在代码中是经常用到的,在此总结一下json字符串和对象及数组之间的相互转化: 1.javascript函数方式: <1> JSON.stringify :把一个对象转换成json字 ...

  10. 【IE6的疯狂之十一】CSS的优先级及!important在IE6下的BUG

    一 css的优先级 今天有人跟我说css hack中用!important来区分ie6,因为ie6不支持!important,是的在很早以前我也是用过这种方法写hack,但是后来就基本不用了.本来我对 ...