输入校验简述
由于Web应用的开放性,网络上所有浏览者都能够自由地使用该应用,这样该应用通过输入页面收集的数据可能很复杂,不仅会包含正常用户的误输入,还可能包含恶意用户的恶意输入。因此应用系统需要将这些非法输入阻止在应用之外。
异常输入,可能导致系统非正常中断,严重者会导致系统崩溃。应用程序需要正常处理表现层接受的各种数据,通常的做法是遇到异常输入时应用程序直接返回,提示浏览者必须重新输入,也就是将那些异常输入过滤掉。绝对异常输入的过滤,就是输入校验,也称为数据校验。
输入校验分为客户端校验和服务器端校验,客户端检验主要是过滤正常用户的误操作,主要通过JavaScript代码完成;服务器端校验是整个应用阻止非法数据的最后防线,主要通过在应用中编程实现。
客户端检验的主要作用是防止正常浏览者的误输入,仅能对输入进行初步过滤;对恶意用户的恶意行为,客户端将无能为力。因此,客户端校验绝不可替代服务器端校验。客户端校验也绝不可少,因为Web应用大部分浏览者都是正常的浏览者,他们的输入可能包含大量的误输入,客户端检验把这些误输入阻止在客户端,从而降低了服务器的负载。struts2提供的输入校验功能既包括服务器端校验,也包括客户端校验。
编写校验规则文件
服务端校验是在数据提交到服务器上之后,在Action处理之前,对客户但提交的数据进行校验 。
struts2提供了基于验证框架的输入校验,Struts2校验框架的配置上可以分为:Java Annotation配置和XML配置文件配置。
Java Annotation配置是指,使用Java Annotation语法,在Java源代码上标记需要校验的内容,和校验的方式。
XML配置文件配置是指,使用XML配置文件配置需要校验的内容和校验方式,在这种校验方式下,所有的输入校验只需要编写简单的配置文件,struts2的验证框架将会负责进行服务端校验和客户端校验。
使用struts2的校验框架进行校验无需对程序代码进行修改,只需编写校验规则文件即可,校验规则文件指定每个表单域应该满足怎样的规则。
先写一段表单代码。
<s:form action="regist">
<s:textfield name="name" lable="用户名"/>
<s:textfield name="pass" lable="密码"/>
<s:textfield name="age" lable="年龄"/>
<s:textfield name="birth" lable="生日"/>
</s:form>
这里定义了4个表单域,分别对应name、pass、age、birth等4个请求参数。我们规定如下规则:
>name和pass只能是字母和数字,且长度必须在4到25之间。
>年龄必须是1到150之间的整数。
>生日必须在1900-01-01和2050-02-21之间
下面是该请求对应的Action代码。
public class RegistAction extends ActionSupport{
//定义4个成员变量封装请求请求参数
private String name;
private String pass;
private int age;
private Date birth;
//下面省略对应的setter和getter方法
.......
}
代码中的Action提供了4个成员变量封装4个请求参数并提供相应的setter和getter方法。该Action继承了ActionSupport类,因此包含了一个execute方法,且该方法直接返回success字符串,此Action不具备任何输入校验功能。
我们通过为该Action指定一个校验规则文件,来利用struts2的输入校验功能对该Action进行校验。下面是本应用使用的输入校验文件RegistAction-validation.xml。
<?xml version="1.0" encoding="utf-8"?>
<!--指定校验配置文件的DTD信息-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<!--校验文件的根元素-->
<validators>
<!--校验Action的name属性-->
<field name="name">
<!--指定name属性必须满足必填规则-->
<field-validator type="requiredstring">
<param name="trim">true<param>
<message>必须输入名字</message>
</field-validator>
<field-validator type="regex">
<param name="regex"><![CDATA[(\w{4,25})]]></param>
<message>您输入的名字只能是字母和数字,且长度必须在4到25之间</message>
</field-validator>
</field>
<!--校验Action的pass属性-->
<field name="pass">
<!--指定pass属性必须满足必填规则-->
<field-validator type="requiredstring">
<param name="tim">true</param>
<message>必须输入密码</message>
</filed-validator>
<field-validator type="regex">
<param name="regex"><![CDATA[(\w{4,25})]]></param>
<message>您输入的密码只能是字母和数字,且长度必须在4到25之间</message>
</field-validator>
</fiell>
<!--指定age属性必须在指定范围内-->
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年纪必须在1到150之间</message>
</field-validator>
</filed>
<!--指定birth属性必须在指定范围内-->
<field name="birth">
<field-validator type="date">
<!--下面指定日期字符串时,必须使用本Locale的日期格式-->
<param name="min">1900-01-01</param>
<param name="max">2050-02-21</param>
<message>生日必须在${min}到${max}之间</message>
</field-validator>
</field>
</validators>
校验规则文件的根元素是< validator../>元素可包含多个< field-validator…/>或< validator…/>元素,这两个的区别是< field-validator…/>是字段校验器的配置风格,而< validator…/>是非非字段校验器的配置风格。
struts2中的每个Action都有一个校验文件,因此该文件的文件名遵守一定的规则:
< Action 名字>-validation.xml
前面Action名字部分可改,后面-validation.xml是固定不变的,且该文件应保存在与Action class文件相同的路径下。例如,本应用的Action class文件保存在WEB-INF/classes.com.afy/app/action路径下,故该校验文件也该保存在该路径下。
与类型转换失败相似,当输入校验失败后,struts2也是自动返回名为“input”的Result,所以需要在struts.xml文件中配置名为“input”Result。下面是本应用的struts.xml文件中Action的配置片段。
<!--用户注册的Action-->
<action name="regist" class="com.afy.app.action.RegisftAction">
<!--类型转换失败、输入校验失败时转入该页面-->
<result name="input">WEB-INF/content/registForm.jsp</result>
<result>WEB-INF/content/show.jsp</result>
</action>
这样就为Action对应的各字段添加了校验规则,而且指定了校验失败后会跳转到registForm.jsp后面,接下来可以在regsitForm.jsp页面中添加< s:fielderror>来输出错误提示。
其他部分不用修改,系统会自动加载该文件,当用户提交请求时,struts2的校验框架会根据该文件对用户请求进行校验。
类型转换失败和输入校验失败的提示信息都被封装成FieldError,并被放入Action Context中,而且校验失败时会将返回input逻辑视图名,且都是用< s:fielderror/>标签来输出错误提示信息。如果开始使用struts2的表单标签来生成表单、表单会自动输出错误提示。
struts2执行数据校验的流程图如下。
国际化提示信息
上面的数据校验中,所有的提示信息都是通过硬编码的方式写在配置文件中的,这种方式显然不利于程序国际化。
当查看每个校验文件时,发现每个< field-validator…/>元素都包含了一个必填的< message…/>子元素,这个子元素中的内容就是校验失败后的提示信息。为了国际化该提示信息,为message元素指定key属性,用于指定国际化提示信息对应的key。
<?xml version="1.0" encoding="utf-8"?>
<!--指定校验配置文件的DTD信息-->
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<!--校验文件的根元素-->
<validators>
<!--校验Action的name属性-->
<field name="name">
<!--指定name属性必须满足必填规则-->
<field-validator type="requiredstring">
<param name="trim">true<param>
<message key="name.requried"/>
</field-validator>
<field-validator type="regex">
<param name="regex"><![CDATA[(\w{4,25})]]></param>
<message key="name.reqex"/>
</field-validator>
</field>
<!--校验Action的pass属性-->
<field name="pass">
<!--指定pass属性必须满足必填规则-->
<field-validator type="requiredstring">
<param name="tim">true</param>
<message key="pass.requried"/>
</filed-validator>
<field-validator type="regex">
<param name="regex"><![CDATA[(\w{4,25})]]></param>
<message key="pass.reqex"/>
</field-validator>
</fiell>
<!--指定age属性必须在指定范围内-->
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message key="age.range"/>
</field-validator>
</filed>
<!--指定birth属性必须在指定范围内-->
<field name="birth">
<field-validator type="date">
<!--下面指定日期字符串时,必须使用本Locale的日期格式-->
<param name="min">1900-01-01</param>
<param name="max">2050-02-21</param>
<message key="birth.range"/>
</field-validator>
</field>
</validators>
上面代码没有之前给出message的内容,而是指定一个key属性,表明当birth字段违反该校验规则时,对应的提示信息是key为birth.range的国际化信息。
本应用的校验文件中指定了许多国际化信息的key,所有需要在国际化资源文件中增加对应的key,即在国际化资源文件中增加Entry(该国家化资源文件也需要用native2ascii处理)。
#违反用户名必须输入的提示信息
name.requried=您必须输入用户名!
#违反用户名必须匹配正则表达式的提示信息
name.regex=您输入的用户名只能是字母和数字,且长度必须在4到25之间!
#违反密码必须输入的提示信息
pass.requried=您必须输入密码!
#违反密码必须匹配正则表达式的提示信息
pass.regex=您输入的密码只能是字母和数字,且长度必须在4到25之间!
#违反年龄必须在指定范围的提示信息
age.range=您的年龄必须在${min}和${max}之间!
#违反生日必须在指定范围的提示信息
birth.range=您的生日必须在${min}和${max}之间!
运行程序,即可看到输入校验的提示信息变为国际化资源文件提供的信息,这就实现了错误提示信息的国际化。
使用客户端校验
在struts2应用中使用客户端校验执行修改两个地方即可:
1. 将输入页面的表单元素改为使用struts2标签来生成表单;
2. 为该< s:form…/>元素增加validate=“true”属性。
修改前面应用的registForm.jsp页面,将页面代码改为如下形式:
<h2>请输入您的注册信息</h2>
<s:fielderror/>
<s:form action="regist" validate="true">
<s:textfield name="name" lable="用户名"/>
<s:textfield name="pass" lable="密码"/>
<s:textfield name="age" lable="年龄"/>
<s:textfield name="birth" lable="生日"/>
</s:form>
只要简单地为struts2的form标签增加validate=“true”,该表单就具备了客户端校验功能。
虽然使用客户端校验,却看不到弹出JavaScript的警告框,这种效果看起来和服务器端校验差不多,但地址栏的地址依然停留在原来页面,并未提交到对应的Action,说明该数据校验过程时客户端校验。
客户端校验依然是基于JavaScript完成的,因为JavaScript脚本本身的限制,有些服务器端校验不能转换成客户端校验,所以不是所有的服务器端都可以转换成客户端校验的。