一、封装请求正文到对象中(重点)

  1.1、静态参数封装

  在struts.xml文件中,给动作类注入值,使用的是setter方法

  

  1.2、动态参数封装

    通过用户表单封装请求正文参数

    1.2.1、动作类作为实体模型

        也就是说动作和实体模型写在一起。

        实体模型:entity。对应数据库中表的记录(类对应的是表的结构,而对象对应的是一条记录)

  

 

    1.2.2、数据模型和动作类分开写

    数据模型:User                                  动作类:Demo2Action

    

 

   

  

    原理:当我们在动作类中的SetUser和getUser分别输出时

      

      所以我们可以自己实例化User对象也可以不用自己实例化。

    1.2.3、模型驱动(开发中采用的方式)     

      要想使用模型驱动,数据模型和动作类必须分开写。
     实现数据模型的步骤:
       1.实现一个ModelDriven的接口
       2.实现接口里的方法getModel()
       3.在使用模型驱动的时候,数据模型必须由我们来完成实例化

       是由一个ModelDriven的拦截器帮我们做的

      

二、基于数据模型封装请求正文编写一个demo

  目录的总体结构

  

  我编写的步骤是这样的先建立数据库表

  根据数据库表写出User的实体类

  写业务层service接口然后业务层的实现类serviceImpl

   写dao层,记得在这里要配置数据库的jar包

  然后写表单jsp,编写struts.xml,编写动作类

三、数据类型转换

 3.1、开发中的情况  

  实际开发中用户输入的数据都是String或String[].

  string/String[]——填充模型(set方法)——POJO(plain old java object)pojo中有java的数据类型

  POJO——获取(get方法)——页面展示String

 3.2、类型转换情况

  写数据:(增、删、改)都是String或string[]转换成其他类型

  读数据:(查)其他数据类型转换成String

 3.3、Struts2提供的常用类型转换

  A.基本数据类型自动转换

  B.日期类型:默认按照本地日期格式转换(yyyy-MM-dd) 

  C.字符串数组:默认用逗号+空格,连接一个字符串

 3.4、自定义类型转换器

  从这张图可以看的出来如果你想要自定义一个类型转换器的话需要集成StrutsTypeConverter类

  3.4.1、创建一个MyTypeConvertor继承StrutsTypeConverter类

package com.jxlg.web.converter;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
 * 需求:
 *     把表单中的MM/dd/yyyy格式的数据转换成日期类型
 *     把数据库的本地日期格式,转化成MM/dd/yyyy形式输出
 * 
 * 自定义子类转换器:
 *     1.编写一个类,继承自StrutsTypeConverter,实现convertFromString和convertToString抽象方法
 */





import org.apache.struts2.util.StrutsTypeConverter;

public class MyTypeConvertor extends StrutsTypeConverter {
    //定义一个类型转换器
    private DateFormat format = new SimpleDateFormat("MM/dd/yyyy");

    /**
     * 把字符串数组中的数据转化成日期类型
     * 
     * 方法参数详解:
     *         Map context:是OGNL的上下文对象,我们暂时不知道。
     *         String[] values:要转换的数据类型
     *         Class toClass:目标类型
     */
    public Object convertFromString(Map context, String[] values, Class toClass) {
        //1.先看看有没有数据
        if(values==null ||values.length==0){
            return null;
        }
        //2.取出数组中的第一个元素
        String date = values[0];
        //3.判断目标类型的字节码是不是日期类型
        if(toClass == java.util.Date.class) {
            //4.使用DateFormat进行转换,并返回转换后的结果
            try {
                return format.parse(date);
            } catch (ParseException e) {
                e.printStackTrace();
                return null;
            }
        }
        return null;
    }
    
    /**
     * 把日期类型转换成字符串
     */
    public String convertToString(Map context, Object o) {
        //1.判断object是不是日期类型
        if(o instanceof Date){
            Date date = (Date)o;
            //2.是日期类型,使用转换器转成指定格式的字符串,并返回
            return format.format(date);
        }
        return null;
    }
    
}
View Code

 

  3.4.2、注册类型转换器

      局部类型转换器:只能指定javabean中的属性来用

      按照属性来注册,在属性所属的javaBean的包下建立一个.properties文件。

      文件名称命名规范:javabean名称-conversion.properties。

    

    

    全局类型转换器(开发中使用这种模式开发)

    按照要转换的数据类型来注册

    存放位置:at the top op classpath(类路径的根路径)  ,建立一个固定名称xwork-conversion-properites的属性文件。

  

 

  3.5、转换失败后的处理(重点)

    当转换失败后,页面的提示

    

  解决办法:配置回显结果试图

 

四、数据验证

  用户的输入验证,必须做,且工作量巨大。

  4.1、验证方式

  客户端验证:javascript

  服务端验证:逻辑验证

  注意:如果客户端和服务端验证二选一,服务端必不可少。

  实际开发中:服务端+客户端都需要验证

  4.2、Struts2的服务端验证

      在struts2的框架中,它也提供了一个Map<表单的字段名,错误提示>,我们要做的:往map中存放错误信息

    4.2.1、编程式验证

      前提:动作类必须继承ActionSupport  

           重写validate方法

      注意:validate方法会在动作方法执行之前,进行验证。

      举例

      第一步:在动作类UserAction中继承AcctionSupport,重写Validate方法

      

        备注:StringUtils是struts框架中org.apache.commons.lang3.StringUtils,jar包中的,增强了String的功能

        

     第二步: register.jsp  

  第三步:配置struts.xml文件

  

  第四步:测试(没有输入用户名直接按注册)

    

  问题:当重写了validate方法,它会对动作类中的所用动作方法进行验证。

    

    在UserAction中添加动作方法

    testValidate.jsp

    

    最后创建一个index.jsp用来测试

    

    结果:

      

    当我们注释掉Validate方法时。

    

 

  解决验证所有动作方法的问题:   

  第一种方式:
    使用@SkipValidation的注解

    就是我们不需要去验证testValidate方法我们就在定义方法的上面进行注释
   第二种方式:
     定义验证方法的名称:validate+动作名称 动作名称的首字符还要大写   

public void validateRegister(){
        if(StringUtils.isEmpty(user.getUsername())){
            //存入错误信息,直接调用父类的addFieldError方法,存入错误信息。第一个参数是表单name属性的值。第二个参数是错误提示
            addFieldError("username", "请输入用户名");
        }

 

  编程式验证弊端:硬编码

  4.2.2、声明式验证

    Struts内置的常用声明式验证器位置:

      

      

    

 

 

    通过编写验证规则的xml文件。需要验证时,编写xml文件,不要验证,就不要写。

    优点:解决了编程式验证的弊端

   A.针对动作类的所有动作进行验证:在动作类所在的包中,建立一个ActionClassName-validation.xml的文件

    测试前要先把前面的validate方法注释和testValidate都注释

<?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">
<!-- 当使用ActionClassName-validation.xml来命名文件时,它是一个动作类验证器。会验证动作类中的所有动作方法 -->
<validators>
    <!-- 基于字段的声明式验证 -->
    <!-- field中name属性指定的是表单name属性的取值 -->
    <field name="username">
        <!-- struts2框架为我们集成了很多的内置验证器。requiredstring会验证输入内容是否为空,是否为空字符串。并且去掉左右空格-->
        <field-validator type="requiredstring">
            <message>请输入用户名</message>
        </field-validator>
    </field>
</validators>

 

  这是一个会验证所有动作方法的,又回到了我们上面编程式验证所遇到的问题。那我们怎么去解决呢?

    解决一:使用@SkipValidation的注解

    解决二:定义一个针对验证动作类中某个动作方法的xml  

    B.针对动作类中的某个动作进行验证:在动作类所在的包中建立一个xml文件,名称为ActionClassName-ActionName-validaton.xml的文件

    内容和上面的一样,只是修改一下名称。

      test:访问index.jsp

        

        

 

    基于验证器的验证

<?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">
<!-- 当使用ActionClassName-ActionName-validation.xml来命名文件时,它是一个指定动作方法的验证器。只会验证指定的动作方法 --> <validators> <field name="username"> <field-validator type="requiredstring"> <message>请输入用户名必须要输入</message> </field-validator> </field> <!-- 基于验证器的验证 --> <validator type="requiredstring"> <!-- 以注入的方式,提供要验证的字段信息 setFieldName("password"); --> <param name="fieldName">password</param> <message>密码必须输入</message> </validator> </validators>

 

     为什么param中的name是filedName呢

    我们可以查看一下源码 

    查看的源码

    找父类

    

五、基于声明式验证的demo

  第一步:编写一个Student.java

package com.jxlg.domain;

import java.io.Serializable;
/**
 * URL:uniform resource locator 统一资源定位符
 * http://localhost:6060/studentManagement/home.jsp
 * 协议            主机                 端口             URI
 * URI:uniform resource identifier 统一资源标识符
 * studentManagement/home.jsp
 * @author Shinelon
 *
 */
public class Student implements Serializable {
    private String username;//不能为null和空字符串。要去空格
    private int age;//整数,介于18-100之间
    private String email;//按照邮箱的格式输入
    private String password;//密码。长度3-8位
    private String repassword;//确认密码,必须和密码一致。写在这里的目的,完全是为了演示验证器的使用。实际开发中根本不会保存确认密码。
    private int score;//成绩,必须是自然数
    private String url;//个人主页:必须符合url格式
    private String gender;//性别
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    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 String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }
    
}
View Code

  第二步:编写一个student.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  <body>
  <s:actionerror/>
  <%-- <s:fielderror />字段错误
  动作错误 --%>
  <%--struts2的form标签,它提供了和原始html表单标签几乎一致的属性
          action:请求地址。直接写动作名称。不用写contextPath
          method:请求方式。在这里可以不写,struts的form表单中默认是post
          enctype:表单编码的MIME类型
      
   --%>
  
  <s:form action="addStudent.action">
      <s:textfield  name="username" label="用户名" />
      <s:textfield  name="age" label="年龄" />
      <s:textfield  name="email" label="邮箱" />
      <s:textfield name="password" label="密码"/>
      <s:textfield name="repassword" label="确认密码"/>
      <s:textfield  name="score" label="成绩"/>
      <s:textfield  name="url" label="个人主页"/>
      <%--list中的取值是生成一个list集合,并往集合中放入元素 --%>
      <s:radio name="gender" list="{'男','女'}" label="性别"/>
      <s:submit value="注册"/>
  </s:form>
  </body>
</html>
View Code

  第三步:编写一个struts.xml,配置addStudent.action

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true"></constant>
    <package name="p1" extends="struts-default">
        
        <!-- 声明式验证器的案例的动作类配置 -->
        <action name="addStudent" class="com.jxlg.web.action.StudentAction" method="addStudent">
            <result name="input">/student.jsp</result>
        </action>
    </package>
</struts>
View Code

  第四步:编写StudentAction动作类继承ActionSupport类和实现modelDriven接口

package com.jxlg.web.action;

import com.jxlg.domain.Student;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class StudentAction extends ActionSupport implements ModelDriven<Student> {

    private Student stu = new Student();
    public Student getModel() {
        return stu;
    }
    
    public String addStudent(){
        return SUCCESS;
    }
    public Student getStu() {
        return stu;
    }
    public void setStu(Student stu) {
        this.stu = stu;
    }

}
View Code

 

  第五步:编写UserAction-register-validation.xml来配置验证器

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
          "-//Apache Struts//XWork Validator 1.0.3//EN"
          "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
    <field name="username">
        <field-validator type="requiredstring">
            <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="email">
        <field-validator type="email">
            <message>请输入正确的邮箱格式</message>
        </field-validator>
    </field>
    <field name="password">
        <field-validator type="requiredstring">
        <!-- 注入取消使用trim -->
        <param name="trim">false</param>
            <message>请输入密码</message>
        </field-validator>
        <field-validator type="stringlength">
            <param name="minLength">3</param>
            <param name="maxLength">8</param>
            <message>请输入3-8位的密码</message>
        </field-validator>
    </field>
    
    <!-- 确认密码和密码必须保持一致,是2个字段的事情,所以要使用基于验证器的声明方式 -->
    <validator type="expression">
        <param name="expression">
        <!-- CDATA为了去掉特殊字符 -->
            <![CDATA[
                password == repassword
            ]]>
        </param>
        <message>两次密码必须一致</message>
    </validator>
    <field name="score">
        <field-validator type="regex">
            <param name="regex">
                /d+
            </param>
            <message>请正确输入成绩</message>
        </field-validator>
    </field>
    <field name="url">
        <field-validator type="url">
            <message>请正确输入url</message>
        </field-validator>
    </field>
    <field name="gender">
        <field-validator type="required">
            <!-- required是一个只验证是否为null的内置验证器,不去除空格 -->
            <message>请输入性别</message>
        </field-validator>
    </field>
</validators>
View Code

  第六步:测试

  

   开发中遇到的问题

  1.在测试自己的student.jsp的页面中出现了错误

  

  经过查看自己的score的标签中的name没有写导致的错误

  2.在测试密码和确认密码的时候发现不一致也不报错

  

  它属于动作错误,所以需要写上面的标签