Struts2的 输入校验

信息基本校验

对于每个web框架输入输入校验都是一个重要的部分,对用户输入的数据进行有效的过滤,是保持系统安全的一方面措施.Struts2也不例外,同样也提供了更简易的输入校验机制,Struts2提供的输入校验有两种方式,一种是硬编码的方式,一种是采用Struts2的输入校验框架进行校验,即采用XML配置的方式进行校验。

下面我们看一上采集硬编码的方式如果校验:

举例说明:需要对一个用户注册的数据进行校验

首先要在myeclipse中创建一Web工程,在src目录中创建struts.xml文件,到struts2的资源包中例子中拷

1.1在WebRoot下面创建一个register.jsp页面,代码

<%@pagelanguage="java"pageEncoding="UTF-8"%>

<%@tagliburi="/struts-tags"prefix="s"%>

<!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>register page</title>

<metahttp-equiv="pragma"content="no-cache">

<metahttp-equiv="cache-control"content="no-cache">

<metahttp-equiv="expires"content="0">

<metahttp-equiv="keywords"content="keyword1,keyword2,keyword3">

<metahttp-equiv="description"content="This is my page">

</head>

<body>

<formaction="register.action"method="post">

<fontcolor="red"size="2"><s:fielderror/></font>

<tableborder="1"cellpadding="0"cellspacing="0"width="500">

<tr>

<tdcolspan="2">用户注册</td>

</tr>

<tr>

<tdwidth="100">username</td>

<td><inputtype="text"name="username"value="${requestScope.username }"/>6-10 cahr</td>

</tr>

<tr>

<td>password</td>

<td><inputtype="password"name="password"value="${requestScope.password }"/></td>

</tr>

<tr>

<td>re-password</td>

<td><inputtype="password"name="repassword"value="${requestScope.repassword }"/></td>

</tr>

<tr>

<td>age</td>

<td><inputtype="text"name="age"value="${requestScope.age }"/>0-150</td>

</tr>

<tr>

<td>birthday</td>

<td><inputtype="text"name="birthday"value="${requestScope.birthday }"/>must be date</td>

</tr>

<tr>

<td>graduation</td>

<td><inputtype="text"name="graduation"value="${requestScope.graduation }"/>must after birthday</td>

</tr>

<tr>

<td></td>

<td><inputtype="submit"value="register"/></td>

</tr>

</table>

</form>

</body>

</html>

上面一个表单使用HTML描述表单内容。显示如:

校验需要,username:不能为空必须是6-10个字符

password repassword不能为空必须是6-10个字符,而且两次输入密码要一致

age必须是0-150数字

birthday graduation都必须是日期,且出生日期要大于毕业日期

1.2下面创建一个RegisterAction来处理用户注册数据代码如下:

package com.snt.struts2.action;

import java.util.Calendar;

import java.util.Date;

import com.opensymphony.xwork2.ActionSupport;

public class RegisterAction extends ActionSupport {

private String username;

private String password;

private String repassword;

private int age;

private Date birthday;

private Date graduation;

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public Date getBirthday() {

return birthday;

}

public void setBirthday(Date birthday) {

this.birthday = birthday;

}

public Date getGraduation() {

return graduation;

}

public void setGraduation(Date graduation) {

this.graduation = graduation;

}

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 getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

@Override

public String execute() throws Exception {

return SUCCESS;

}

public String abc()throws Exception{

System.out.println("abc method invoke!");

return SUCCESS;

}

public String xyz()throws Exception{

System.out.println("abc method invoke!");

return SUCCESS;

}

public void validateAbc(){

System.out.println("valide abc()");

}

public void validateXyz(){

System.out.println("valide xyz()");

}

/***

*当遇到类型转化时,struts2自动生成一条错误信息,放到

* fielderrors中

*/

@Override

public void validate() {

//当用户直接访问action时,action的属性都是null

//防止NullPointException

System.out.println("validate()...........");

if(null==username || username.length()<6 || username.length()>10){

this.addFieldError("username", "username is invalid");

}

if(null==password ||password.length()<6 || password.length()>10){this.addFieldError("password", "password is invalid!");

}else if(null==repassword ||repassword.length()<6 || repassword.length()>10){

this.addFieldError("password", "password is invalid!");

}else if(!password.equals(repassword)){

this.addFieldError("password", "tow password is not be same!");

}

if(age<0 || age>150){

this.addFieldError("age", "age is invalid!");

}

if(null==birthday){

this.addFieldError("birthday", "birthday invalid!");

}

if(null==graduation){

this.addFieldError("graduation", "graduationinvalid!");

}

if(null!=birthday && null!=graduation){

Calendar c1=Calendar.getInstance();

c1.setTime(birthday);

Calendar c2=Calendar.getInstance();

c2.setTime(graduation);

if(!c1.before(c2)){

this.addFieldError("birthday","birthday shoud before graduation!");

}

}

}

}

红色的是检验的代码,我创建的Action继承了ActionSupport类,ActionSupport双继承了Validateable和ValidationAware接口,validate()是Validateable接口中的方法,我们继承Validateable接口,实现validate()就可以在调用Action的execute()方法之前,执行validate()方法进行数据检验。根据的我们的检验逻辑如果某个属性出错了,会产生一个相应的错误信息,以属性名为Key值,以错误信息为值,形成一个键值对通过addFieldError()方法放在一个保存错误信息的Map中,下面介绍一个addFieldError()方法的原理:

下面是ActionSupport中的一些方法:

private final ValidationAwareSupport validationAware=new ValidationAwareSupport();

//struts2是采用上面的ValidationAwareSupport这个类完成的类完成的。

//设置字段错误信息

publicvoidsetFieldErrors(Map<String, List<String>> errorMap) {

validationAware.setFieldErrors(errorMap);

}

//获取所有字段错误信息

publicMap<String, List<String>> getFieldErrors() {

returnvalidationAware.getFieldErrors();

}

//我们从上面的方法大致可以看出来,struts2是如下保存错误信息:

每个字段的错误是这样保存的每个字段上可能产生多条错误信息,所有保存时是:字段名为Key,

将所有这个字段的错误形成一个List作为value值,放在Map的一条记录中。

//添加一条字段错误信息

publicvoidaddFieldError(String fieldName, String errorMessage) {

validationAware.addFieldError(fieldName, errorMessage);

}

下面是ValidationAwareSupport部分源代码:

publicclassValidationAwareSupportimplementsValidationAware, Serializable {

privateCollection<String>actionErrors;//保存Action级别的错误信息Collection

privateMap<String, List<String>>fieldErrors;//保存Field级别的错误信息Map

//添加一条字段错误的详细代码

publicsynchronizedvoidaddFieldError(String fieldName, String errorMessage) {

//获取所有字段错误的Map

finalMap<String, List<String>> errors =internalGetFieldErrors();

//取出对应字段的错误信息列表

List<String> thisFieldErrors = errors.get(fieldName);

//如果之前没有错误,新创建一个List保存错误信息

if(thisFieldErrors ==null) {

thisFieldErrors =newArrayList<String>();

errors.put(fieldName, thisFieldErrors);

}

//将错误信息添加到列表中

thisFieldErrors.add(errorMessage);

}

//初始化存储错误的信息的Map

privateMap<String, List<String>>internalGetFieldErrors() {

if(fieldErrors==null) {

fieldErrors=newLinkedHashMap<String, List<String>>();

}

returnfieldErrors;

}

}

1.3在struts.xml文件本配置RegisterAction如下:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstrutsPUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<constantname="struts.custom.i18n.resources"value="message"/>

<packagename="struts2"extends="struts-default">

<actionname="register"

class="com.snt.struts2.action.RegisterAction">

<resultname="success">/success.jsp</result>

<resultname="input">/register2.jsp</result>

</action>

</package>

</struts>

运行应用测试:

OK,验证信息成功,但是我们发现错误信息中出现一些我们没有见过的信息,

比如:Invalid field value for field "birthday".这样的信息,这是由Struts2自身的校验给出来的信息,默认的情况下,如果遇到类型转化失败的问题,struts2会自动添加一条错误信息,使用addFieldError()添加一条错误信息。但是这样的信息并不是我们想要的,因为这些信并不友好,我们又不好控制,所有我们要想办法取消这些信息,但是这些是信息是struts2自身的一些拦截器实现的功能,又不容易取掉(应该可以去掉,可以找到这个拦截器,重写它去掉这部分功能,自己再组织拦截器栈),所以考虑将其替换掉;因此Struts2提供一此替换这些信息的方法:1可以通过替换国际化资源配置文件,来达到目的,具体做法:

[1]在strrts.xml配置中指定国际化资源文件常量

在<package >标签下加一条

<constantname="struts.custom.i18n.resources"value="message"/>

Name代表常量名,固定的;value代表指定的资源配置文件

这样配置会覆盖default.properties中struts2的配置常量

[2]在classes目录下,创建工程时可以在src目录下创建一个message.properties文件,文件名要和上面的vlaue值对应,这个文件是全局的配置,输入内容如下:

xwork.default.invalid.fieldvalue={0}error

等号左边的是固定不变的,右边是{0}一个占位符,将来会被错误的字段名替换;

也是当一个字段发生错误了,比如:age转化时错了,就会显示age error的信息

运行测试,这样显示就友好一点了!如下显示:

但是还存在一些问题,信息出错时,错误信息显示太死板了,我们想给一些字段加上个性化的信息。Struts2也提供对应的方法:

[1]在需要输入校验的Action同目标下,创建一个与Action同名的资源文件,比如:RegisterAction.properties这个文件将成功局部的资源配置文件,它里面的配置可以替换全局的配置;输入以下内容

invalid.fieldvalue.age=/u5e74/u9f84/u8f93/u5165/u4fe1/u606f/u4e0d/u6b63/u786e

invalid.fieldvalue.birthday=/u65e5/u671f/u8f93/u5165/u9519/u8bef

invalid.fieldvalue.graduation=/u6bd5/u4e1a/u65f6/u95f4/u65e5/u671f/u8f93/u5165/u4e0d/u6b63/u786e

这样的信息,注意等号左边的invalid.fieldvalue是固定的,之后跟的是字段名;右边是要显示的错误信息;采用的是Unicode码表示的!因为是中文,所有需要使用JDK中的native2ascii命令将汉字翻译成unicode码,即可使用。Native2ascii命令在JDK安装目录/bin下面。

这样运行即可以显示友好的中文信息:

回显表单数据,排除信息重复显示

OK,上面的基本的输入校验已经完成,当然还存在一些问题,比如:错误信息提示还是有重复的,当数据错误时不能回显,这都是一些不友好的做法!我们下面一步步完善!

为了添加回显的功能,我用采用Struts2标签库来很容易地实现!当然使用HTML,通过JSP的EL表达式也可以实现回显,上面写过了,介这种做法并不好,显示过程中可能会出现问题,特别是日期显示那块!

下面我们采用struts2的标签库来重构页面:

创建一个register2.jsp页面,导入struts2的标签库,代码如下:

<%@pagelanguage="java"pageEncoding="UTF-8"%>

<%@ taglib uri="/struts-tags" prefix="s" %>

<!DOCTYPEHTMLPUBLIC"-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

<head>

<title>register page</title>

<metahttp-equiv="pragma"content="no-cache">

<metahttp-equiv="cache-control"content="no-cache">

<metahttp-equiv="expires"content="0">

<metahttp-equiv="keywords"content="keyword1,keyword2,keyword3">

<metahttp-equiv="description"content="This is my page">

</head>

<!--回显-->

<body>

<s:actionerror cssStyle="color:red;"/>

<hr>

<s:fielderror cssStyle="color:red;"/>

<s:formaction="register2"theme="simple">

<tableborder="1"cellpadding="0"cellspacing="0">

<tr>

<td>username</td>

<td><s:textfieldname="username"label="usernmae"/></td>

</tr>

<tr>

<td>password</td>

<td><s:passwordname="password"label="password"/></td>

</tr>

<tr>

<td>repassword</td>

<td><s:passwordname="repassword"label="repassword"/></td>

</tr>

<tr>

<td>age</td>

<td><s:textfieldname="age"label="age"/></td>

</tr>

<tr>

<td>birthday</td>

<td><s:textfieldname="birthday"label="birthday"/></td>

</tr>

<tr>

<td>graduation</td>

<td><s:textfieldname="graduation"label="graduation"/></td>

</tr>

<tr>

<td>&nbsp;</td>

<td><s:submit/></td>

</tr>

</table>

</s:form>

</body>

</html>

上面我们采用了表格布局,当然如果不采用表格的话,struts2在翻译struts2标签是,会为每个标签添加一个单元格将控件括起来。这是struts表单默认的显示样式。

显示如下:

运行测试仪可以正常回显数据:

错误级别

上面我们提到过,错误信息的级别,其实Struts2中的错误有两种级别:

1.Action级别的错误

2.Field级别的错误

而Struts2只有判断到两种错误级别的信息都没有的情况,才认为是输入校验通过。

查看VlidationAwareSupport类的原代码:高亮显示的一句代码,可以确定struts2是这样做的。

publicsynchronizedbooleanhasActionErrors() {

return(actionErrors!=null) && !actionErrors.isEmpty();

}

publicsynchronizedbooleanhasActionMessages() {

return(actionMessages!=null) && !actionMessages.isEmpty();

}

publicsynchronizedbooleanhasErrors() {

return(hasActionErrors() || hasFieldErrors());

}

publicsynchronizedbooleanhasFieldErrors() {

return(fieldErrors!=null) && !fieldErrors.isEmpty();

}

下面我们重写一下validate()方法,将所有添加addFieldError()的信息,转化成addActionError();

如下:

publicvoidvalidateExecute() {

//当用户直接访问action时,action的属性都是null

//防止NullPointException

System.out.println("validate()...........");

if(null==username||username.length()<6 ||username.length()>10){

this.addActionError("username invalid!");

}

if(null==password||password.length()<6 ||password.length()>10){

this.addActionError("password is invalid!");

}elseif(null==repassword||repassword.length()<6 ||repassword.length()>10){

this.addActionError("password is invalid!");

}elseif(!password.equals(repassword)){

this.addActionError("tow password is not be same!");

}

if(age<=0 ||age>150){

System.out.println("ERROR");

this.addActionError("age is invalid!");

}

if(null!=birthday&&null!=graduation){

Calendar c1=Calendar.getInstance();

c1.setTime(birthday);

Calendar c2=Calendar.getInstance();

c2.setTime(graduation);

if(!c1.before(c2)){

this.addActionError("birthday shoud before graduation!");

}

}

再将运行界面时,我们发现数据校验明明是错误的却不会显示错误信息;这是因为Struts2标签默认只会显示Field级别的错误信息。要想显示Action级别的信息,需要在表单一加上一句;

<s:actionerror cssStyle="color:red;"/>

OK,这样信息显示出来,但还是有问题的,我们发现Struts2默认的表单布局,错误信息部会显示在控件的上方,而且种级别的信息显示有重复的,这需要我们做两件事情,来避免!

1.需要重新考虑validate()校验方法的代码,比如:前类型转化失败的情况下,一些校验就不用做了;即便这样有时还会是有重复的,另一种办法就是改变表单的主题,Struts2为每个表单显示都定义了一个主题,默认是HTML主题,其它的还有Ajax和Simple ,我们可以把它改成Simple即可以自己定义布局的主题,是这样,一旦改成了theme=”simple”它的错误信息就不会显示了。这也是我们使用这个主题的原因。

这些操作已经在上面jsp页面中做了!

在真正的开发中,需要根据情况采用修改主题,或重构验证方法,来正确显示错误信息。

多方法情况下输入校验

OK,上面讲的都是针对Action中execute()方法之前的验证,我们知道struts2中也提供了一个Action中多个方法,处理多种请求的机制。可能通过,struts.xml配置action中,加一个method属性来实现!如:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstrutsPUBLIC

"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<constantname="struts.custom.i18n.resources"value="message"/>

<packagename="struts2"extends="struts-default">

<actionname="register1"class="com.snt.struts2.action.RegisterAction"method="register1">

<resultname="success">/success.jsp</result>

<resultname="input">/register.jsp</result>

</action>

<actionname="register2"

class="com.snt.struts2.action.RegisterAction">

<resultname="success">/success.jsp</result>

<resultname="input">/register2.jsp</result>

</action>

</package>

</struts>

这样配置是可以的,使用register1的请求会调用Action中的register()方法;

使用regiser2的请求会调用Acrtion中的execute()方法。

但这时输入校验如何处理呢?Struts2是这样处理的:

对于execute()方法,它会执行validate()方法进行输入校验,对于register()它会调用validateRegiser()方法进行输入校验,但是对于register()的情况比较特别,它调用validateRegister()方法后,它还会调用validate()方法。也是就说validate()方法总会被调用的!对于这种情况呢,当然是对我们输入校验会产生干扰的!~

如何处理这样的问题呢?提供以下解决方案:

1.空实现valiate()方法,但是这样有问题,如何校验execute()方法呢?可以选择不用execute()方法

2.不提供validate()方法,也就是空实现,但要提供一个validateExecute()方法来校验execute()方法!

转载来自:

http://blog.csdn.net/lilp_ndsc/article/details/4054251

猜你喜欢

转载自fgh2011.iteye.com/blog/1496695
今日推荐