深入理解Struts2----数据校验

     在表现层的数据处理方面主要分为两种类型,一种是类型转换,这点我们上篇已经简单介绍过,另外一种则是我们本篇文章将要介绍的:数据校验。对于我们的web应用,我们经常需要和用户进行交互收集用户信息,那么无论是用户误操作还是恶意攻击,这些错误的信息一旦被传入到后台,小则导致程序异常关闭,大则导致整个系统瘫痪。数据校验就是对用户的输入做一层过滤,保护我们的系统免受侵入。下面我们开始介绍本篇的内容,主要包括以下几小节:

  • 一个简单的例子(用于全局把握整个校验过程)
  • 两种校验配置风格
  • 为不同Action处理逻辑配置不同的校验配置
  • 详解struts2框架内置的几种校验器
  • 自定义校验器

一、一个简单的例子
     在详细介绍数据校验的每一步骤之前,我们先通过一个简单的例子从全局范围把握下整个数据校验流程都需要哪些文件,各个步骤执行的顺序。强调的是从全局粗略的感受下,不用在意具体的代码。

//登录表单页面,信息提交到loginAction
<html>
  <head>
    <title></title>
  </head>
  <body>
    <s:form method="POST" action="/login">
      <s:textfield name="name" label="姓名"/>
      <s:textfield name="age" label="年龄"/>
      <s:submit value="提交"/>
    </s:form>
    
  </body>
</html>
//定义一个action
public class LoginAction extends ActionSupport {

    private String name;
    private int age;
    public void setName(String n){
        this.name= n;
    }
    public String getName(){
        return this.name;
    }
    public void setAge(int a){
        this.age = a;
    }
    public int getAge(){
        return this.age;
    }

    public String execute(){
        return SUCCESS;
    }
}
//创建校验文件并键入以下内容
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator 1.0.2//EN"
        "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">

<validators>
    <field name="name">
        <field-validator type="requiredstring">
            <param name="trim">true</param>
            <message>姓名不能为空</message>
        </field-validator>
    </field>
    <field name="age">
        <field-validator type="requiredstring">
            <param name="trim">true</param>
            <message>年龄不能为空</message>
        </field-validator>
    </field>
</validators>

我们首先从login这个表单页面提交表单信息到loginAction中的属性,然后框架会查找是否有校验规则文件,如果有则执行它。在校验的过程中,如果校验失败会跳转到处理结果为 input 视图页面,这里和上篇介绍的类型转换是一样的,我们也一般是需要为其指定一个input视图页面的。在我们上述的校验文件中,我们规定两个属性的值不能为空,如果为空则该数据不符合要求,框架会封装错误信息并跳转到input视图页面。下面我们看看上述代码的运行截图:

这里写图片描述

这里写图片描述

至此我们简单了解了数据校验整个过程大致需要的文件及其作用,当然该例中还有很多细节之处没有展现出来,因为目的只是从整体上直观感受下,详细的内容将在下面的小节中展现。

二、两种校验配置风格
     下面我们从校验文件开始看起。首先一点,校验文件的命名是有要求的并且一般一个校验文件只服务一个Action,所以该文件的命名规则如下:

<ActionName>-validation.xml

所以上述我们为LoginAction创建的校验文件名为:LoginAction-validation.xml。该文件有个根元素 validators ,我们的属性校验元素是其子元素。下面我们介绍第一种配置校验文件的方式,上述的例子就是这种方式,该种方式使用field 作为一级子元素,该元素将对应于Action实例中实际的属性,它有一个name属性,该属性就是用于指定此field元素配置的是Action的哪个实例属性,有几个实例属性就应该有几个field元素。

我们由field元素可以定位到Action实例中具体的某个属性,使用field-validator元素为给属性指定校验器(Struts默认提供的检验器,具体有关内置的校验器后文详细介绍),param 元素用于指定校验的参数,message元素用于指定不符合校验规则时输出的信息。我们可以根据不同的处理需要为Action实例属性指定不同的校验器,当然我们也是可以自定义校验器来校验属性的数值的。

上面介绍的是用field元素来配置的数据校验规则。下面我们介绍第二种配置风格,使用validator取代field作为一级子元素,用fieldName属性指定对应的Action实例属性,对于上面的配置,我们也可以改写为:

<validators>
    <validator type="requiredstring">
        <param name="fieldName">name</param>
        <param name="trim">true</param>
        <message>姓名不能为空</message>
    </validator>
    <validator type="requiredstring">
        <param name="fieldName">age</param>
        <param name="trim">true</param>
        <message>年龄不能为空</message>
    </validator>
</validators>

我们看到该方式的配置每个属性用一个validator元素表示,通过param元素的fieldName属性对应实际的Action实例属性,其余内容和第一种方式类似。他们之间的区别相信大家对比之后也能明白,只是书写方式不同,都能达到效果,大家可以根据自己喜好选择使用哪种方式,由于个人喜好,下文都会采用第一种方式进行介绍。

三、为不同Action处理逻辑配置不同的校验配置
     我们的某个具体的Action类在很多情况下是可以用于多个不同的处理逻辑的,例如某个action既可以处理用户注册请求也可以处理用户登录请求,但是对于这两种截然不同的请求,我们的数据校验却不尽相同,下面看个例子:

public class LoginAction extends ActionSupport {

    private String name;
    private String pass1;
    private String pass2;
    public void setName(String n){
        this.name= n;
    }
    public String getName(){
        return this.name;
    }
    public void setPass1(String p1){
        this.pass1 = p1;
    }
    public String getPass1(){
        return this.pass1;
    }
    public void setPass2(String p2){
        this.pass2 = p2;
    }
    public String getPass2(){
        return this.pass2;
    }

    public String execute(){
        return SUCCESS;
    }
}
//Struts.xml中指定该类用于响应两个请求
<struts>
    <package name="my" extends="struts-default" namespace="/">
        <action name="login" class="MyPackage.LoginAction">
            <result name="success">/index.jsp</result>
            <result name="input">/input.jsp</result>
        </action>
        <action name="regist" class="MyPackage.LoginAction">
            <result name="success">/index.jsp</result>
            <result name="input">/input.jsp</result>
        </action>
    </package>
</struts>

此时我们需要创建两个数据校验文件,一个用于校验请求login,一个用于校验请求regist。当然为了框架能够快速搜索到,这里两个文件的命名也是有要求的,规则如下:

<ActionName>-<ActionAliasName>-validation.xml

对应于上述的两个文件名为:LoginAction-login-validation.xml和LoginAction-regist-validation.xml。下面给出此例中上述两个文件的内容:

//LoginAction-login-validation.xml局部内容
<validators>
    <field name="name">
        <field-validator type="requiredstring">
            <param name="trim">true</param>
            <message>姓名不能为空</message>
        </field-validator>
    </field>
    <field name="pass1">
        <field-validator type="requiredstring">
            <param name="trim">true</param>
            <message>密码不能为空</message>
        </field-validator>
    </field>
</validators>
//LoginAction-regist-validation.xml局部内容
//该校验文件为pass1属性添加了一个fieldexpression校验器,用于校验是否和pass2相等
<validators>
    <field name="name">
        <field-validator type="requiredstring">
            <param name="trim">true</param>
            <message>姓名不能为空</message>
        </field-validator>
    </field>
    <field name="pass1">
        <field-validator type="requiredstring">
            <param name="trim">true</param>
            <message>密码不能为空</message>
        </field-validator>
        <field-validator type="fieldexpression">
            <param name="expression"><![CDATA[(pass1==pass2)]]></param>
            <message>两次密码不同</message>
        </field-validator>
    </field>
    <field name="pass2">
        <field-validator type="requiredstring">
            <param name="trim">true</param>
            <message>密码不能为空</message>
        </field-validator>
    </field>
</validators>

上述的两个文件依然是需要存放在和Action相同目录下,框架会根据不同的请求而去检索相应的校验文件,下面我们看表单页面:

//注册页面
  <body>
    <s:form method="POST" action="/regist">
      <s:textfield name="name" label="姓名"/>
      <s:textfield name="pass1" label="密码"/>
      <s:textfield name="pass2" label="密码"/>
      <s:submit value="提交"/>
    </s:form>
  </body>
这里写图片描述

这里写图片描述

而对于login登录逻辑来说,

  <body>
    <s:form method="POST" action="/login">
      <s:textfield name="name" label="姓名"/>
      <s:textfield name="pass1" label="密码"/>
      <s:submit value="提交"/>
    </s:form>
  </body>
这里写图片描述

这里写图片描述

只需要用户名和密码不为空即可,并没有别的校验逻辑,可能上述的例子并不是十分恰当,但是却很清晰的展示了这种校验分离的处理过程。对于校验文件中的内容,我们将在下文详细介绍,此处不必纠结。最后需要强调一点的是,当我们为每个不同的处理逻辑配置相对应的校验文件时,原来的那个LoginAction-validation.xml文件则会被作为默认的校验文件,当LoginAction-login-validation.xml或者LoginAction-regist-validation.xml执行之后,会自动继续执行该校验文件,也就会导致校验了两遍,所以一般会在该文件中添加该Action的通用校验代码。

四、详解struts2框架内置校验器
     有关框架的内置校验器,我们在上文中一直都使用,但是没有详细的介绍,本小节就专门介绍这些内置校验器的使用,下一小节将介绍如何自定义一个校验器。首先解压xwork-core-2.3.32.jar,从com\opensymphony\xwork2\validator\validators中找到default.xml并打开:

这里写图片描述

这是框架内建立的所有校验器,我们会介绍其中的部分, 剩余的一些校验器由于某些局限性不经常使用,所以此处就不在介绍。

下面看第一种校验器,必填校验器。该校验器要求指定字段的值非空(null)。该校验器的使用比较简单,此处不再演示。

第二种校验器,必填字符串校验器。该校验器要求字段的值非空并且长度要大于0。即字段不能是""。该校验器要求比第一种必填校验器严格一点。它还具有一个参数:trim。该参数用于剔除字段中前后的空白,默认值为true。这一点也是比较容易理解的,此处不再赘述。

第三种校验器,整数校验器。对于Action中字段类型为int,long,short的情况,我们可以使用该校验器来要求该字段的值必须存在于指定的范围内。它有两个参数,min,max,一个是指定该字段的值可能出现的最小值,一个则是指定该字段的值可能出现的最大值。下面看个例子:

//LoginAction中添加age属性的代码并未贴出
//此处指定了该age属性必须在0-100之间
<validators>
    <field name="age">
        <field-validator type="int">
            <param name="min">0</param>
            <param name="max">100</param>
            <message>输入的年龄必须在指定0-100之间</message>
        </field-validator>
    </field>
</validators>
这里写图片描述

这里写图片描述

第四种校验器,日期校验器。该校验器用于规定日期类型的属性值可能出现的范围。有两个参数,min和max。两者分别指定日期最小值和最大值。下面看一个例子:

//LoginAction中添加属性date
<validators>
    <field name="date">
        <field-validator type="date">
            <param name="min">1990-01-01</param>
            <param name="max">2017-5-19</param>
            <message>日期范围为:1990-01-01到2017-5-19</message>
        </field-validator>
    </field>
</validators>
这里写图片描述

这里写图片描述

第五种校验器,字段表达式校验器,fieldexpression。它要求该字段满足一个基于ognl的表达式。该校验器具有一个参数,expression,该参数指定了一个表达式。下面我们看一个具体的例子:

//表达式要写在 <![CDATA[....]]> 内
<validators>
    <field name="pass1">
        <field-validator type="fieldexpression">
            <param name="expression"><![CDATA[(pass1 == pass2)]]></param>
            <message>无法匹配表达式</message>
        </field-validator>
    </field>
</validators>
这里写图片描述

这里写图片描述

需要注意一点的是我们可以使用ognl表达式从ValueStack中取数据进行比较,但是ognl表达式本身必须被写在<![CDATA[....]]>中。至于为什么,作者还没参透。

第六种校验器,字符串长度校验器。该校验器比较简单,和之前介绍的几种很是类似,主要有几个参数,maxLength,minLength,trim。相信大家也都知道他们什么意思,此处不再赘述。

第七种校验器,正则表达式校验器。该校验器有两个参数,regex代表正则表达式内容,caseSensitive指定校验时是否考虑大小写。具体使用情况比较容易,此处不再赘述。下面我们看自定义校验器。

五、自定义校验器
     相比于使用Struts2内置校验器,自定义一个校验器反而简单些。我们只需要重写ActionSupport类中的一个方法即可:

  public void validate() {}

下面我们看一个例子:

//在LoginAction中添加如下一个方法
    public void validate(){
        if(name.isEmpty()){
            addFieldError("name","用户名不能为空");
        }
        if (!pass1.equals(pass2)){
            addFieldError("pass1","两次输入的密码必须相同");
        }
    }

该方法用于判断字段name是否为空,如果为空打包错误信息添加到FieldError中,判断两次输入的密码是否一致,如果不一致打包错误信息添加到FieldError中。在方法结束时,框架会去查看FieldError是否为空,如果不为空说明校验出错,跳转视图input页面。下面我们看上述代码的运行截图:

这里写图片描述

这里写图片描述

从运行的结果看,自定义校验器和使用框架内置校验器都能完成数据校验的工作,但是个人认为自定义校验方式反而显得过程简单。对于之前介绍的一个Action类响应多个请求时候对数据校验的不同形态,在我们自定义校验器中也是可以实现的,只是定义的方法名有所区别,例如:

响应login处理逻辑的自定义校验方法命名为:validateLogin()
响应regist处理逻辑的自定义校验方法命名为:validateRegist()

至此,我们简单介绍完了有关Struts2框架的数据校验部分的内容,总结的粗糙,望大家多多评论指点!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,850评论 6 13
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 11月22日新闻简报 星期三 美丽的鱼为您推送工作愉快 生活喜乐! 1、MH370失联案庭前会议确认5被告,家属最...
    睡遍全世界阅读 103评论 0 0
  • 福特公司意识到需要发展AI和机器人团队 一个新的研发团队将会帮助福特赶上其自动驾驶的竞争对手 原文链接:https...
    肥寒925阅读 135评论 0 1