Struts2学习-4 表单校验

什么时候使用的是OGNL标签:
1.通用的标签
2.UI的标签
3.ajax的标签:Ajax Tags
以上3中标签有可能会使用的是OGNL标签
 
为什么学习Struts2呢?
a、企业中用的多
b、灵活--->可配置(越灵活的东东越散,感觉就越乱)
 
框架就是使程序员按部就班的开发程序(重复性的编码工作弄成了框架),省下的时间去研究业务(需求)。
 
表单效验
普通式的开发,用声明式的,细致的控制,用编程式的验证
1、编程式验证:
用代码,表达式的方式
前提:动作类一般要求继承ActionSupport
struts.xml配置文件中,对要验证的动作,需要提供name="input"的结果视图(回显)
<action name="RegUser" class="com.itheima.action.UserAction" method="RegUser">
    <result type="dispatcher" name="success">/WEB-INF/pages/main.jsp</result>
    <result type="dispatcher" name="error">/WEB-INF/pages/commons/error.jsp</result>
    <!-- 出现错误时转向的页面:回显 -->
    <result name="input">/WEB-INF/pages/regist.jsp</result>
</action>
 
针对所有动作进行验证:需要覆盖public void validate()方法,方法内部如果不满足要求,调用addFieldError填充信息.
    对于这种写法,不需要验证的动作方法,请使用@SkipValidation
不能验证先RegUserUI,是先进行拦截,但是这个类还没有实例化,是不能进行拦截的。
使用org.apache.commons.lang3.StringUtils工具包简化代码。针对某些动作的验证是没有简化的代码.
    @SkipValidation//用在不需要验证的动作方法上
    public String RegUserUI() {
        return SUCCESS;
    }
 //对所有的动作方法进行校验
 public void validate(){
 //写你的校验代码ActionSupport里面有addFieldError()方法,把错误信息存起来.
     if(StringUtils.isEmpty(user.getUsername())){
         addFieldError("username""请输入用户名");//向一个Map中存储错误消息。何时返回input视图,是由该Map中有无信息决定的。
     }
 }
 
针对某些动作进行验证:    public String regUser(){}
    验证方法:public Void validateRegUser(){};注意方法中的首字母要大写   RegUser();
//针对某个动作方法进行校验
public void validateRegUser() {
    // 写你的校验代码
    if (user.getUsername() == null || user.getUsername().equals("")) {
        addFieldError("username""请输入用户名");
    }
}
 
2、声明式验证:
(开发的时候,可以不写验证,后续增加声明式验证,写配置文件即可,编码式验证需要开始就要写)
        2.1struts2中已经内置一些验证器:(com.opensymphony.xwork2.validator.validators.default.xml)
            注解验证配置文件的设置
            2.1.1,打开xwork-core-2.*.jar包中的xwork-validator-1.*.dtd文件,复制表头
<!DOCTYPE validators PUBLIC 
          "-//Apache Struts//XWork Validator 1.0//EN"
          "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
            2.1.2,进行文件配置。具体参考如下。
 
        2.2如何使用内置验证器:
           对所有的动作方法都进行验证: 在动作类相同的包中:动作类名-validation.xml使用。
            
            针对某些动作进行验证:动作类名-动作别名-validation.xml(动作别名指Struts.xml中的action的name)

配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC 
          "-//Apache Struts//XWork Validator 1.0//EN"
          "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
    <!-- 针对字段的验证:方式一(建议使用).在一个字段上加上多个验证规则-->
    <field name="username" >
        <field-validator type="requiredstring">
            <message>用户名是必须的</message>
        </field-validator>
    </field>
     <!-- 针对字段的验证:方式二.
     <validator type="requiredstring">
     <!-- 校验器中有trim方法,当把这个方法设置为false,那么用户名前后可以有空格 -->
         <param name="trim">false</param>
         <param name="fieldName">username</param>
         <message>必须的用户名</message>
     </validator>-->
     <!-- 在使用配置文件的時候,只使用一种配置方式 -->
     <field name="password">
         <field-validator type="strongpassword">
             <message>密码不够强壮</message>
         </field-validator>
     </field>
</validators>
    3、自定义声明式验证器:(具体看案例)
        a、编写一个类,继承FieldValidatorSupport,复写public void validate(Object object) throws ValidationException
        b、定义自定义的验证器:在WEB-INF/classes目录下建立一个validators.xml的配置文件。
 
案例:表单验证
先写struts配置页面
<package name="p1" namespace="/user" extends="struts-default">
    <action name="RegUserUI" class="com.itheima.action.UserAction" method="RegUserUI">
        <result type="dispatcher" name="success">/WEB-INF/pages/regist.jsp</result>
    </action>
    <action name="RegUser" class="com.itheima.action.UserAction" method="RegUser">
        <result type="dispatcher" name="success">/WEB-INF/pages/main.jsp</result>
        <result type="dispatcher" name="error">/WEB-INF/pages/commons/error.jsp</result>
        <!-- 出现错误时转向的页面:回显 -->
        <result name="input">/WEB-INF/pages/regist.jsp</result>
    </action>
</package>
 
写一个regist.jsp页面
<s:fielderror fieldName="password" />标签标示在此显示错误信息,
里面的属性标示显示该属性的错误信息,如果不写就显示所有错误信息
requiredLabel="true"表示在用户名旁边加 *(单纯加*,没有其他作用); requiredPosition="left"表示 * 在左边
  <head>
  <!-- 这个标签对是错误提示的样式模板 -->
    <s:head/>
    <title>新用户注册</title>
    <!-- 以下样式出现在s:head标签之下,覆盖了此标签的样式,所以这个为准 -->
    <style type="text/css">
        .errorMessage{
            colorred;
        }
    </style>
<s:fielderror fieldName="password" />
<s:form action="RegUser" namespace="/user">
    <s:textfield key="hello" label="用户名" name="username" requiredLabel="true" requiredPosition="left"></s:textfield>
    <s:textfield label="昵称" name="nick"></s:textfield>
    <s:password label="密码" name="password"></s:password>
</s:form>
 
写一个UserAction类
public class UserAction extends ActionSupport implements ModelDriven<User> {
    private User user = new User();
    private Map<String, String> hobbies = new HashMap<String, String>();
    public Map<String, String> getHobbies() {
        hobbies.put("eat""吃饭");
        hobbies.put("sleep""睡觉");
        hobbies.put("study""学java");
        return hobbies;
    }
    public void setHobbies(Map<String, String> hobbies) {
        this.hobbies = hobbies;
    }
    public String RegUser() {
        try {
            System.out.println(user);
            // 调用service,保存数据
            System.out.println("调用后台service,保存数据到数据库中");
            return SUCCESS;
        } catch (Exception e) {
            e.printStackTrace();
            return ERROR;
        }
    }
    @SkipValidation//用在不需要验证的动作方法上
    public String RegUserUI() {
        return SUCCESS;
    }
     //对所有的动作方法进行校验
//     public void validate(){
//     //写你的校验代码
//         if(StringUtils.isEmpty(user.getUsername())){
//             addFieldError("username", "请输入用户名");//向一个Map中存储错误消息。何时返回input视图,是由该Map中有无信息决定的。
//         }
//     }
    //针对某个动作方法进行校验
//    public void validateRegUser() {
//        // 写你的校验代码
//        if (user.getUsername() == null || user.getUsername().equals("")) {
//            addFieldError("username", "请输入用户名");
//        }
//    }
    public User getModel() {
        return user;
    }
}
 
使用声明式校验器
<validators>
    <!-- 针对字段的验证:方式一(建议使用).在一个字段上加上多个验证规则-->
    <field name="username" >
        <field-validator type="requiredstring">
            <message>用户名是必须的</message>
        </field-validator>
    </field>
     <!-- 针对字段的验证:方式二.
     <validator type="requiredstring">
     <!-- 校验器中有trim方法,当把这个方法设置为false,那么用户名前后可以有空格 -->
         <param name="trim">false</param>
         <param name="fieldName">username</param>
         <message>必须的用户名</message>
     </validator>-->
     <!-- 在使用配置文件的時候,只使用一种配置方式 -->
</validators>
 
建立StrongpasswordFieldValidate(编写一个自定义校验器)
    public class StrongpasswordFieldValidate extends FieldValidatorSupport {
        //object就是当前执行的动作类的实例
        public void validate(Object object) throws ValidationException {
            String fieldName = getFieldName();
            Object value = getFieldValue(fieldName, object);
            //验证
            if(!(value instanceof String)){
                addFieldError(fieldName, object);
            }else{
                String s = (String)value;
                if(!isStrong(s)){
                    addFieldError(fieldName, object);
                }
            }
        }
        //判断s是否强大
        private static final String GROUP1 = "abcdefghijklmnopqrstuvwxyz";
        private static final String GROUP2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        private static final String GROUP3 = "0123456789";
        private boolean isStrong(String s) {
            boolean ok1 = false;
            boolean ok2 = false;
            boolean ok3 = false;
            int length = s.length();
            for(int i=0;i<length;i++){
                if(ok1&&ok2&&ok3)
                    break;
                String character = s.substring(i,i+1);
                if(GROUP1.contains(character)){
                    ok1 = true;
                    continue;
                }
                if(GROUP2.contains(character)){
                    ok2 = true;
                    continue;
                }
                if(GROUP3.contains(character)){
                    ok3 = true;
                    continue;
                }
            }
            return ok1&&ok2&&ok3;
        }
    }
对自定义的校验器进行配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
          "-//Apache Struts//XWork Validator Definition 1.0//EN"
          "http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
<validators>
    <validator name="strongpassword" class="com.itheima.validators.StrongpasswordFieldValidate"/>
</validators>
使用自定义的校验器(struts.xml中配置)
 <field name="password">
     <field-validator type="strongpassword">
         <message>密码不够强壮</message>
     </field-validator>
 </field>
 
类型转换器
先写配置文件struts.xml
<result name="input">regist.jsp</result>这个input验证也需要,转换也需要.这里的目的是回显数据
    <package name="p1" namespace="/user" extends="struts-default">
        <action name="RegUserUI" class="com.itheima.action.UserAction" method="RegUserUI">
            <result type="dispatcher" name="success">/WEB-INF/pages/regist.jsp</result>
        </action>
        <action name="RegUser" class="com.itheima.action.UserAction" method="RegUser">
            <result type="dispatcher" name="success">/WEB-INF/pages/main.jsp</result>
            <result type="dispatcher" name="error">/WEB-INF/pages/commons/error.jsp</result>
            <result name="input">/WEB-INF/pages/regist.jsp</result>
        </action>
    </package>
 
写一个JavaBean,定义四种类型的变量,演示.
public class User implements Serializable {
    private String username;//字符串类型
    private Integer age;//不要写int型,因为页面会默认为0
    private Date birthday;//Date型,转换器会自动转换String
    private Point luckpoint = new Point(438,520);//自定义类型
 
写一个动作类UserAction
public class UserAction extends ActionSupport implements ModelDriven<User> {
    private User user = new User();
    public String RegUserUI() {
        return SUCCESS;
    }
    public String RegUser() {
        try {
            System.out.println(user);
            // 调用service,保存数据
            System.out.println("调用后台service,保存数据到数据库中");
            return SUCCESS;
        } catch (Exception e) {
            e.printStackTrace();
            return ERROR;
        }
    }
    public User getModel() {
        return user;
    }
}
 
类型转换错误消息设置
1,在动作类所在的包中创建一个文件,命名规则ClassName.properties

2,在该文件中增加以下内容:
invalid.fieldvalue.fieldName=你的消息   fieldName是动作类中对应的字段.
 
3,或者在国际化的消息资源包中增加以下内容,原理和国际化的东东一样

注意:
转换的时候,String和Integer基本类型的不用管,因为struts会自己转换
Date类型的也不用管,但是需要注意Date是地区敏感信息,需要与本地Date格式一致.最好是选择.
 自定义类的类型转换
先建立一个Point
public class Point {
    private Integer x;
    private Integer y;
    public Point(){}
    public Point(Integer x,Integer y){
        this.x = x;
        this.y = y;
    }
再建立一个PointConveor自定义类型转换器,继承StrutsTypeConverter,重写两个方法


 
public class PointConvertor extends StrutsTypeConverter {
    public PointConvertor(){
        System.out.println("调用了默认的构造方法");
    }
    //字符串类型转换成其他类型:String--->Point
    public Object convertFromString(Map context, String[] values, Class toClass) {
        if(toClass==Point.class){
            String xy = values[0];
            String s[] = xy.split("\\,");
            return new Point(Integer.parseInt(s[0]),Integer.parseInt(s[1]));
        }
        return null;
    }
    //其他类型转换成字符串:用在显示的时候.struts对此有bug
    public String convertToString(Map context, Object o) {
        if(o instanceof Point){
            Point p = (Point)o;
            return p.getX()+","+p.getY();
        }
        return null;
    }
}
自定义类型转换器完成后,需要对其进行配置,建立User
局部类型转换器设置
 
 
全局类型转换器设置
在src中建立xwork-conversion.properties,里面的值为(JavaBean类全名=转换器类全名)
 
注册类型转换器:
     局部类型转换器:
     要转换的属性所在的类相同的包下:建立类名-conversion.properties的配置文件
luckpoint=com.itheima.convertor.PointConvertor内容:字段名=自定义的类型转换器的类全名

        全局类型转换器:
            在WEB-INF/classes目录下,建立一个名为xwork-conversion.properties的配置文件
 
com.itheima.domain.Point=com.itheima.convertor.PointConvertor //内容:目标类型的类全名=自定义的类型转换器的类全名
 
文件的上传和下载
文件上传是由拦截器来实现的
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
先设置配置文件struts.xml(别忘了打开静态开关)
<struts>
    <constant name="struts.devMode" value="true" />
    <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    <package name="default" extends="struts-default">
        <action name="uploadUI" class="com.itheima.action.UploadAction" method="uploadUI">
            <result>/upload.jsp</result>
        </action>
        <action name="upload" class="com.itheima.action.UploadAction" method="upload">
            <result>/1.jsp</result>
            <result name="input">/upload.jsp</result>
            <result name="error">/error.jsp</result>
        </action>
        <action name="download" class="com.itheima.action.UploadAction" method="download">
            <result type="stream">
                <param name="contentType">application/octet-stream</param>
                <!-- 下载中文名称的文件:在配置文件中使用OGNL表达式对中文文件名进行URL编码-->
                <param name="contentDisposition">attachment;filename=${@java.net.URLEncoder@encode(photoFileName,'UTF-8')}</param>
                <param name="inputName">myInputStream</param>
            </result>
        </action>
    </package>
</struts>
 
写一个动作类,UploadAction继承ActionSupport.
建立4个变量,并生成set和get方法.其中photoContentType的set方法,能够被框架执行,并存进来 MIME类型
public class UploadAction extends ActionSupport {
    private File photo;
    private String photoFileName;
    private String photoContentType;
    private InputStream myInputStream;
  
    public String download() throws FileNotFoundException{
        photoFileName = "整容广告.jpg";
        //就是myInputStream赋值
        String storeDirectory = ServletActionContext.getServletContext().getRealPath("/files/整容广告.jpg");
        myInputStream = new FileInputStream(storeDirectory);
        return SUCCESS;
    }
    public String upload(){
        try {
            InputStream in = new FileInputStream(photo);
            String storeDirectory = ServletActionContext.getServletContext().getRealPath("/files");
            OutputStream out = new FileOutputStream(storeDirectory+"/"+photoFileName);
            int len = -1;
            byte b[] = new byte[1024];
            while((len=in.read(b))!=-1){
                out.write(b, 0, len);
            }
            in.close();
            out.close();
            return SUCCESS;
        }  catch (Exception e) {
            e.printStackTrace();
            return ERROR;
        }
    }
上传jsp页面
  <body>
    <s:form action="upload" enctype="multipart/form-data">
        <s:file label="靓照" name="photo"></s:file>
        <s:submit value="上传"></s:submit>
    </s:form>
  </body>
 
使用标准插件---JFreeChart使用(插件其实就是一个结果视图)
所需jar包:
/jfreechart-1.0.13.jar/
jcommon-1.0.16.jar/
struts2-jfreechart-plugin-2.1.8.1.jar
建立 ChartAction动作类
public class ChartAction extends ActionSupport {
    private JFreeChart chart;
    public JFreeChart getChart() {
        return chart;
    }
    public String execute() throws Exception {
        ValueAxis xAxis = new NumberAxis("年度");
        ValueAxis yAxis = new NumberAxis("产值");
        XYSeries xySeries = new XYSeries("绿豆");
        xySeries.add(0,300);
        xySeries.add(1,200);
        xySeries.add(2,400);
        xySeries.add(3,500);
        xySeries.add(4,600);
        xySeries.add(5,500);
        xySeries.add(6,800);
        xySeries.add(7,1000);
        xySeries.add(8,1100);
        XYSeriesCollection xyDataset = new XYSeriesCollection(xySeries);
        XYPlot xyPlot = new XYPlot(xyDataset,xAxis,yAxis,new StandardXYItemRenderer(StandardXYItemRenderer.SHAPES_AND_LINES));
        chart = new JFreeChart(xyPlot);
        return SUCCESS;
    }
}
进行配置
<action name="showchart" class="com.itheima.action.ChartAction">
    <result name="success" type="chart">
        <param name="height">400</param>
              <param name="width">600</param>
    </result>
</action>
jsp页面
  <body>
    上传成功!<br/>
    <s:url action="showchart" var="url"></s:url>
    <img alt="图图" src="${url}">
  </body>
 
使用插件,必须要先知道插件的用途,一般只要组织数据就好了,没必要深究.
 
伪装URL地址
建立JavaBean,User,建立3个变量,写构造方法get和set方法,还有toString方法
public class User {
    private Integer id;
    private String username;
    private String nick;
    public User(){}
    public User(Integer id, String username, String nick) {
        super();
        this.id = id;
        this.username = username;
        this.nick = nick;
    }
写一个动作类
public class UserAction extends ActionSupport{
    private Integer id;
    private List<User> users;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public List<User> getUsers() {
        users = new ArrayList<User>();
        users.add(new User(1, "wangchao""超人"));
        users.add(new User(2,"renmin","敏姐"));
        users.add(new User(3,"renmin","敏姐"));
        users.add(new User(4,"renmin","敏姐"));
        users.add(new User(5,"renmin","敏姐"));
        return users;
    }
    public String showAll(){
        return SUCCESS;
    }
    public String queryOne(){
        //根据id的值,调用serivce,找到那个用户
        List<User> us = getUsers();
        for(User u:us){
            if(id.equals(u.getId())){
                System.out.println(u);
            }
        }
        return null;
    }
}
进行struts.xml文件配置
    <constant name="struts.devMode" value="true" />
    <constant name="struts.action.extension" value="html"></constant>
    <constant name="struts.enable.SlashesInActionNames" value="true"></constant>
<!--     <constant name="struts.mapper.alwaysSelectFullNamespace" value="true"></constant> -->
    <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    <package name="default" extends="struts-default">
        <action name="showAll" class="com.itheima.action.UserAction" method="showAll">
            <result>/list.jsp</result>
        </action>
        <action name="users/*" class="com.itheima.action.UserAction" method="queryOne">
            <param name="id">{1}</param>
        </action>
    </package>
写一个jsp页面
  <body>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>USERNAME</th>
            <th>NICK</th>
            <th>OPERATION</th>
        </tr>
        <s:iterator value="users" status="s">
            <tr bgcolor='<s:property value="#s.odd?'#c3f3c3':'#f3c3f3'"/>'>
                <td>
                    <s:property value="id"/>
                </td>
                <td>${username}</td>
                <td>${nick}</td>
                <td>
                    <a href="${pageContext.request.contextPath}/users/${id}.html">查询一个</a>
                </td>
            </tr>
        </s:iterator>
    </table>
  </body>
 
防止表单重复提交
a在输入表单中加入<s:token/>标签
b在要防止重复提交的动作配置中加入token拦截器
c提交会出错,需要配置一个结果
d如果在返回的页面中要显示错误消息提示,使用s:actionErrors
e要覆盖默认的提示,请在国际消息资源文件中加入struts.messages.invalid.token=你的提示信息
 
1,写一个配置文件需要加入token拦截器
<!-- Redirect After Post(提交完后,请进行重定向,可以防止表单重复提交)
<action name="addUser" class="com.itheima.action.UserAction" method="addUser">
    <result type="redirect">/success.jsp</result>
</action>
 -->
 <action name="addUser" class="com.itheima.action.UserAction" method="addUser">
     <interceptor-ref name="token"></interceptor-ref>
     <interceptor-ref name="defaultStack"></interceptor-ref>
    <result>/success.jsp</result>
    <!-- 第一个结果视图是invalid.token -->
    <result name="invalid.token">/regist.jsp</result>
</action>
2.写jsp页面
  <body>
      <s:actionerror/><!--显示错误消息提示.
    <s:form action="addUser">
        <s:textfield label="用户名" name="username"></s:textfield>
        <s:textfield label="昵称" name="nick"></s:textfield>
        <s:submit name="submit" value="注册"></s:submit>
        <s:token></s:token>
    </s:form>
  </body>
 

猜你喜欢

转载自blog.csdn.net/qq_34801169/article/details/80673113