Struts2第二天

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页面

内容如下:

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

        <!-- 属性驱动1actoin作为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获取数据

//属性驱动方法2

//通过一个独立的model对象来回去参数并封装

//分析:先获取了user对象,然后通过页面的ognl表达式提交参数,然后通过user对象的gettersetter方法获取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的属性来封装数据。因此,这两种都统称为 属性驱动封装数据

 

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

 

 

  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/>

        手机号码:<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方法!!!

 

  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. 重点和小结

 

posted @ 2017-01-10 21:48  beyondcj  阅读(382)  评论(1编辑  收藏  举报