首页  ·  知识 ·  编程语言
Struts2教程2
佚名  本站原创  Java  编辑:dezai  图片来源:网络
在Struts2中最简单的验证数据的方法是使用validate.我们从ActionSupport类的源代码中可以看到,ActionSupport类实现了一个Validateable接口。这个接

  在Struts2中最简单的验证数据的方法是使用validate.我们从ActionSupport类的源代码中可以看到,ActionSupport类实现了一个Validateable接口。这个接口只有一个validate方法。如果Action类实现了这个接口,Struts2在调用execute方法之前首先会调用这个方法,我们可以在validate方法中验证,如果发生错误,可以根据错误的level选择字段级错误,还是动作级错误。并且可使用addFieldError或addActionError加入相应的错误信息,如果存在Action或Field错误,Struts2会返回“input”(这个并不用开发人员写,由Struts2自动返回),如果返回了“input”,Struts2就不会再调用execute方法了。如果不存在错误信息,Struts2在最后会调用execute方法。

    这两个add方法和ActionErrors类中的add方法类似,只是add方法的错误信息需要一个ActionMessage对象,比较麻烦。除了加入错误信息外,还可以使用addActionMessage方法加入成功提交后的信息。当提交成功后,可以显示这些信息。

    以上三个add方法都在ValidationAware接口中定义,并且在ActionSupport类中有一个默认的实现。其实,在ActionSupport类中的实现实际上是调用了ValidationAwareSupport中的相应的方法,也就是这三个add方法是在ValidationAwareSupport类中实现的,代码如下:


privatefinalValidationAwareSupportvalidationAware=newValidationAwareSupport();
publicvoidaddActionError(StringanErrorMessage)
{ validationAware.addActionError(anErrorMessage);
}
publicvoidaddActionMessage(StringaMessage)
{
validationAware.addActionMessage(aMessage);
}
publicvoidaddFieldError(StringfieldName,StringerrorMessage)
{
validationAware.addFieldError(fieldName,errorMessage);
}


    下面我们来实现一个简单的验证程序,来体验一个validate方法的使用。

    先来在Web根目录建立一个主页面(validate.jsp),代码如下:


%@pagelanguage="java"import="java.util.*"pageEncoding="GBK"%><%@taglibprefix="s"uri="/struts-tags"%><html> <head><title>验证数据</title> </head>  <body><s:actionerror/><s:actionmessage/><s:formaction="validate.action" theme="simple">输入内容:<s:textfieldname="msg"/><s:fielderrorkey="msg.hello"/><br/><s:submit/></s:form> </body></html>


    在上面的代码中,使用了Struts2的tag:<s:actionerror>、<s:fielderror>和<s:actionmessage>,分别用来显示动作错误信息,字段错误信息,和动作信息。如果信息为空,则不显示。

    现在我们来实现一个动作类,代码如下:

    packageaction;


importjavax.servlet.http.*;
importcom.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.interceptor.*;
publicclassValidateActionextendsActionSupport
{
privateStringmsg;
publicStringexecute()
{
System.out.println(SUCCESS);
returnSUCCESS;
}
publicvoidvalidate()
{
if(!msg.equalsIgnoreCase("hello"))
{
System.out.println(INPUT);
this.addFieldError("msg.hello","必须输入hello!");
this.addActionError("处理动作失败!");
}
else
{
this.addActionMessage("提交成功");
}
}
publicStringgetMsg()
{
returnmsg;
}
publicvoidsetMsg(Stringmsg)
{
this.msg=msg;
}
}

    大家从上面的代码可以看出,Field错误需要一个key(一般用来表示是哪一个属性出的错误),而Action错误和Action消息只要提供一个信息字符串就可以了。

    最后来配置一下这个Action,代码如下:


<packagename="demo"extends="struts-default"><actionname="validate"class="action.ValidateAction"><resultname="success">/error/validate.jsp</result><resultname="input">/error/validate.jsp</result></action></package>


    假设应用程序的上下文路径为demo,则可通过如下的URL来测试程序:

    http://localhost:8080/demo/validate.jsp

    我们还可以使用ValidationAware接口的其他方法(由ValidationAwareSupport类实现)获得或设置字段错误信息、动作错误信息以及动作消息。如hasActionErrors方法判断是否存在动作层的错误,getFieldErrors获得字段错误信息(一个Map对象)。下面是ValidationAware接口提供的所有的方法:

    packagecom.opensymphony.xwork2;


importjava.util.Collection;importjava.util.Map;publicinterfaceValidationAware{voidsetActionErrors(CollectionerrorMessages);CollectiongetActionErrors();voidsetActionMessages(Collectionmessages);CollectiongetActionMessages();voidsetFieldErrors(MaperrorMap);MapgetFieldErrors();voidaddActionError(StringanErrorMessage);voidaddActionMessage(StringaMessage);voidaddFieldError(StringfieldName,StringerrorMessage);booleanhasActionErrors();booleanhasActionMessages();booleanhasErrors();booleanhasFieldErrors();}
  在《Struts2教程4:使用validate方法验证数据》中曾讲到使用validate方法来验证客户端提交的数据,但如果使用validate方法就会将验证代码和正常的逻辑代码混在一起,但这样做并不利于代码维护,而且也很难将过些代码用于其他程序的验证。在Struts2中为我们提供了一个Validation框架,这个框架和Struts1.x提供的Validation框架类似,也是通过XML文件进行配置。

    服务端验证

    下面将给出一个例子来演示如何使用Struts2的validation框架来进行服务端验证。我们可以按着如下四步来编写这个程序:

    【第1步】建立Action类(NewValidateAction.java)

    packageaction;


importcom.opensymphony.xwork2.ActionSupport;
publicclassNewValidateActionextendsActionSupport
{
privateStringmsg; //必须输入
privateintage; //在13和20之间
publicStringgetMsg()
{
returnmsg;
}
publicvoidsetMsg(Stringmsg)
{
this.msg=msg;
}
publicintgetAge()
{
returnage;
}
publicvoidsetAge(intage)
{
this.age=age;
}
}


    下面我们来验证msg和age属性。

    【第2步】配置Action类,struts.xml的代码

    如下:


<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEstrutsPUBLIC"-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd"><struts><packagename="demo"extends="struts-default"namespace="/test"><actionname="new_validate"class="action.NewValidateAction"><resultname="input">/validate_form.jsp</result><resultname="success">/validate_form.jsp</result></action></package></struts>


【第3步】编写验证规则配置文件

    这是一个基于XML的配置文件,和struts1.x中的validator框架的验证规则配置文件类似。但一般放到和要验证的.class文件在同一目录下,而且配置文件名要使用如下两个规则中的一个来命名:


<ActionClassName>-validation.xml<ActionClassName>-<ActionAliasName>-validation.xml


    其中<ActionAliasName>就是struts.xml中<ation>的name属性值。在本例中我们使用第一种命名规则,所以文件名是NewValidateAction-validation.xml。文件的内容如下:


<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEvalidatorsPUBLIC"-//OpenSymphonyGroup//XWorkValidator1.0.2//EN""http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"><validators><fieldname="msg"><field-validatortype="requiredstring"><message>请输入信息</message></field-validator></field><fieldname="age"><field-validatortype="int"><paramname="min">13</param><paramname="max">20</param><message>必须在13至20之间</message></field-validator></field></validators>


    这个文件使用了两个规则:requiredstring(必须输入)和int(确定整型范围)。关于其他更详细的验证规则,请读者访问http://struts.apache.org/2.0.11.1/docs/validation.html来查看。

    【第4步】编写数据录入JSP页

    在Web根目录中建立一个validate_form.jsp文件,代码如下:


%@pagelanguage="java"import="java.util.*"pageEncoding="GBK"%><%@taglibprefix="s"uri="/struts-tags"%><linkrel="stylesheet"type="text/css"href="<s:urlvalue="/styles/styles.css"/>"><html> <head><title>验证数据</title> </head>  <body><s:formaction="new_validate"namespace="/test"><s:textfieldname="msg"label="姓名" /><s:textfieldname="age"label="年龄"/><s:submit/></s:form> </body> </html>


    大家要注意一下,如果在struts.xml的<package>标签中指定namespace属性,需要在<s:form>中也将namespace和action分开写,如上面代码所示。不能将其连在一起,Struts2需要分开的action和namespace。如下面的代码是错误的:


<s:form action="/test/new_validate" >  ... ...</s:form>


    在上面的程序中还使用了一个styles.css来定制错误信息的风格。代码如下:


.label {font-style:italic; }.errorLabel {font-style:italic;color:red; }.errorMessage {font-weight:bold; color:red; }


    需要在Web根目录中建立一个styles目录,并将styles.css

    假设Web工程的上下文路径是validation,可以使用如下的URL来测试这个程序:

    http://localhost:8080/validation/validate_form.jsp
 客户端验证

    在Struts2中实现客户端验证非常简单,只需要在<s:form>中加入一个validate属性,值为true。如<s:form validate="true" ... > ... </form>即可。
验证嵌套属性

    有一类特殊的属性,即这个属性的类型是另外一个JavaBean,如有一个User类,代码如下:

    packagedata;


publicclassUser{privateStringname;privateintage;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}}


    在NewValidateAction类中加一个user属性,代码如下:

    packageaction;


importcom.opensymphony.xwork2.ActionSupport;importdata.User;publicclassNewValidateActionextendsActionSupport{privateStringmsg;privateintage;privateUseruser;publicStringgetMsg(){returnmsg;}publicvoidsetMsg(Stringmsg){this.msg=msg;}publicintgetAge(){returnage;}publicvoidsetAge(intage){this.age=age;}publicUsergetUser(){returnuser;}publicvoidsetUser(Useruser){this.user=user;}}

    如果要验证NewValidateAction中的user属性,可以使用visitor验证器。操作过程如下:

    首先在NewValidateAction-validation.xml中加入一个<field>标签,代码如下:


<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEvalidatorsPUBLIC"-//OpenSymphonyGroup//XWorkValidator1.0.2//EN""http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"><validators>……<fieldname="user"><field-validatortype="visitor"><paramname="context">abc</param><paramname="appendPrefix">true</param><message>User:</message></field-validator></field></validators>


    其中context参数将作为验证User类属性的文件名的一部分,如user属性返回一个User对象,那么用于验证User对象属性的文件名为User-abc-validation.xml。这个文件要和User.class文件在同一个目录中。appendPrefix表示是否在字段里加user,如果为true,Struts2就会使用user.name在form提交的数据中查找要验证的数据。这个属性的默认值是true。如果出错,Struts2会将<message>标签中的信息加到User-abc-validation.xml文件中的相应错误信息前面。

    User-abc-validation.xml文件的内容如下:


<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEvalidatorsPUBLIC"-//OpenSymphonyGroup//XWorkValidator1.0.2//EN""http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"><validators><fieldname="name"><field-validatortype="requiredstring"><message>请输入name</message></field-validator></field><fieldname="age"><field-validatortype="int"><paramname="min">5</param><paramname="max">20</param><message>必须在5至20之间</message></field-validator></field></validators>


    下面修改validate_form.jsp,代码如下:


<s:formvalidate="true"action="new_validate"namespace="/test"><s:textfieldname="msg"label="姓名" /><s:textfieldname="age"label="年龄"/><s:textfieldname="user.name"label="姓名1" /><s:textfieldname="user.age"label="年龄1"/><s:submit/></s:form>


    大家可以看到,最后两个<s:textfield>的name属性是user.name和user.age,正好是加了前缀的。

   现在重新访问http://localhost:8080/validation/validate_form.jsp 经笔者测试,使用visitor无法以客户端验证的方式来验证user属性,但NewValidateAction中其他的属性可以使用客户端测试。在struts1.x Action类的execute方法中,有四个参数,其中两个就是response和request。而在Struts2中,并没有任何参数,因此,就不能简单地从execute方法获得HttpServletResponse或HttpServletRequest对象了。

    但在Struts2 Action类中仍然有很多方法可以获得这些对象。下面就列出四种获得这些对象的方法。

    【方法1】使用Struts2 Aware拦截器

    这种方法需要Action类实现相应的拦截器接口。如我们要获得HttpServletResponse对象,需要实现org.apache.struts2.interceptor.ServletResponseAware接口,代码如下:

    packageaction;


importcom.opensymphony.xwork2.ActionSupport;
importjavax.servlet.http.*;
importorg.apache.struts2.interceptor.*;
publicclassMyActionextendsActionSupportimplementsServletResponseAware
{
privatejavax.servlet.http.HttpServletResponseresponse;
//获得HttpServletResponse对象
publicvoidsetServletResponse(HttpServletResponseresponse)
{
this.response=response;
}
publicStringexecute()throwsException
{
response.getWriter().write("实现ServletResponseAware接口");
}
}


    在上面的代码中,MyAction实现了一个ServletResponseAware接口,并且实现了setServletResponse方法。如果一个动作类实现了ServletResponseAware接口,Struts2在调用execute方法之前,就会先调用setServletResponse方法,并将response参数传入这个方法。如果想获得HttpServletRequest、HttpSession和Cookie等对象,动作类可以分别实现ServletRequestAware、SessionAware和CookiesAware等接口。这些接口都在org.apache.struts2.interceptor包中。

    如果要获得请求参数,动作类可以实现org.apache.struts2.interceptor. ParameterAware接口,但如果只想判断某个参数是否存在,也可以实现com.opensymphony.xwork2.interceptor. ParameterNameAware接口。这个接口有一个acceptableParameterName方法,当Struts2获得一个请求参数时,就会调用一次。读者可以在这个方法中将所有的请求参数记录下来,以便以后使用。这个方法的定义如下:


boolean acceptableParameterName(String parameterName);


    【方法2】使用RequestAware拦截器

    这种方法和第1种方法类似。动作类需要实现一个org.apache.struts2.interceptor.RequestAware接口。所不同的是RequestAware将获得一个com.opensymphony.xwork2.util.OgnlValueStack对象,这个对象可以获得response、request及其他的一些信息。代码如下所示:

    packageaction;


importjava.util.Map;importorg.apache.struts2.*;importcom.opensymphony.xwork2.ActionSupport;importjavax.servlet.http.*;importcom.opensymphony.xwork2.util.*;importorg.apache.struts2.interceptor.*;publicclassFirstActionextendsActionSupportimplementsRequestAware{privateMaprequest;privateHttpServletResponseresponse;publicvoidsetRequest(Maprequest){this.request=request;}publicStringexecute()throwsException{java.util.Set<String>keys=request.keySet();//枚举所有的key值。实际上只有一个key:struts.valueStackfor(Stringkey:keys)System.out.println(key);//获得OgnlValueStack对象OgnlValueStackstack=(OgnlValueStack)myRequest.get("struts.valueStack");//获得HttpServletResponse对象response= (HttpServletResponse)stack.getContext().get(StrutsStatics.HTTP_RESPONSE);response.getWriter().write("实现RequestAware接口");}}


    我们也可以使用StrutsStatics.HTTP_REQUEST、StrutsStatics.PAGE_CONTEXT来获得HttpServletRequest和PageContext对象。这种方法有些麻烦,一般很少用,读者可以作为一个参考。

    【方法3】使用ActionContext类

    这种方法比较简单,我们可以通过org.apache.struts2.ActionContext类的get方法获得相应的对象。代码如下:


HttpServletResponse response = (HttpServletResponse)ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);HttpServletRequest request = (HttpServletRequest)ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_REQUEST);


    【方法4】使用ServletActionContext类

    Struts2为我们提供了一种最简单的方法获得HttpServletResponse及其他对象。这就是org.apache.struts2.ServletActionContext类。我们可以直接使用ServletActionContext类的getRequest、getResponse方法来获得HttpServletRequest、HttpServletResponse对象。代码如下:


HttpServletResponse response = ServletActionContext.getResponse()response.getWriter().write("hello world");


    从这四种方法来看,最后一种是最简单的,读者可以根据自己的需要和要求来选择使用哪一种方法来获得这些对象。
上传单个文件

    上传文件是很多Web程序都具有的功能。在Struts1.x中已经提供了用于上传文件的组件。而在Struts2中提供了一个更为容易操作的上传文件组件。所不同的是,Struts1.x的上传组件需要一个ActionForm来传递文件,而Struts2的上传组件是一个拦截器(这个拦截器不用配置,是自动装载的)。在本文中先介绍一下如何用struts2上传单个文件,最后介绍一下用struts2上传任意多个文件。

    要用Struts2实现上传单个文件的功能非常容易实现,只要使用普通的Action即可。但为了获得一些上传文件的信息,如上传文件名、上传文件类型以及上传文件的Stream对象,就需要按着一定规则来为Action类增加一些getter和setter方法。

    在Struts2中,用于获得和设置java.io.File对象(Struts2将文件上传到临时路径,并使用java.io.File打开这个临时文件)的方法是getUpload和setUpload。获得和设置文件名的方法是getUploadFileName和setUploadFileName,获得和设置上传文件内容类型的方法是getUploadContentType和setUploadContentType。下面是用于上传的动作类的完整代码:
packageaction;


importjava.io.*;
importcom.opensymphony.xwork2.ActionSupport;
publicclassUploadActionextendsActionSupport
{
privateFileupload;
privateStringfileName;
privateStringuploadContentType;

publicStringgetUploadFileName()
{
returnfileName;
}
publicvoidsetUploadFileName(StringfileName)
{
this.fileName=fileName;
}
publicFilegetUpload()
{
returnupload;
}
publicvoidsetUpload(Fileupload)
{
this.upload=upload;
}
publicvoidsetUploadContentType(StringcontentType)
{
this.uploadContentType=contentType;

}

publicStringgetUploadContentType()
{
returnthis.uploadContentType;
}
publicStringexecute()throwsException
{ 
java.io.InputStreamis=newjava.io.FileInputStream(upload);
java.io.OutputStreamos=newjava.io.FileOutputStream("d:upload"+fileName);
bytebuffer[]=newbyte[8192];
intcount=0;
while((count=is.read(buffer))>0)
{
os.write(buffer,0,count);
}
os.close();
is.close();
returnSUCCESS;
}
}

    在execute方法中的实现代码就很简单了,只是从临时文件复制到指定的路径(在这里是d:upload)中。上传文件的临时目录的默认值是javax.servlet.context.tempdir的值,但可以通过struts.properties(和struts.xml在同一个目录下)的struts.multipart.saveDir属性设置。Struts2上传文件的默认大小限制是2M(2097152字节),也可以通过struts.properties文件中的struts.multipart.maxSize修改,如struts.multipart.maxSize=2048 表示一次上传文件的总大小不能超过2K字节。

    下面的代码是上传文件的JSP页面代码:


%@pagelanguage="java"import="java.util.*"pageEncoding="GBK"%
%@taglibprefix="s"uri="/struts-tags"%
<html>
<head>
<title>上传单个文件</title>
</head>
<body>
<s:formaction="upload"namespace="/test"
enctype="multipart/form-data">
<s:filename="upload"label="输入要上传的文件名"/>
<s:submitvalue="上传"/>
</s:form>
</body>
</html>


    也可以在success.jsp页中通过<s:property>获得文件的属性(文件名和文件内容类型),代码如下:


<s:property value="uploadFileName"/>
上传任意多个文件

    在Struts2中,上传任意多个文件也非常容易实现。首先,要想上传任意多个文件,需要在客户端使用DOM技术生成任意多个标签。name属性值都相同。代码如下:

<html>
<head>
<scriptlanguage="javascript">
functionaddComponent()
{
varuploadHTML=document.createElement("<inputtype='file' name='upload'/>");
document.getElementById("files").appendChild(uploadHTML);
uploadHTML=document.createElement("<p/>");
document.getElementById("files").appendChild(uploadHTML);
}
</script>
</head>
<body>
<inputtype="button"onclick="addComponent();"value="添加文件"/>
<br/>
<formonsubmit="returntrue;"action="/struts2/test/upload.action"
method="post"enctype="multipart/form-data">
<spanid="files"><inputtype='file'name='upload'/>
<p/>
</span>
<inputtype="submit"value="上传"/>
</form>
</body>
</html>    上面的javascript代码可以生成任意多个<input type=’file’>标签,name的值都为file(要注意的是,上面的javascript代码只适合于IE浏览器,firefox等其他浏览器需要使用他的代码)。至于Action类,和上传单个文件的Action类基本一至,只需要将三个属性的类型改为List即可。代码如下:

    packageaction;

importjava.io.*;
importcom.opensymphony.xwork2.ActionSupport;
publicclassUploadMoreActionextendsActionSupport
{
privatejava.util.List<File>uploads;
privatejava.util.List<String>fileNames;
privatejava.util.List<String>uploadContentTypes;
publicjava.util.List<String>getUploadFileName()
{
returnfileNames;
}
publicvoidsetUploadFileName(java.util.List<String>fileNames)
{
this.fileNames=fileNames;
}
publicjava.util.List<File>getUpload()
{
returnuploads;
}
publicvoidsetUpload(java.util.List<File>uploads)
{
this.uploads=uploads;
}
publicvoidsetUploadContentType(java.util.List<String>contentTypes)
{
this.uploadContentTypes=contentTypes;
}
publicjava.util.List<String>getUploadContentType()
{
returnthis.uploadContentTypes;
}
publicStringexecute()throwsException
{
if(uploads!=null)
{
inti=0;
for(;i<uploads.size();i++)
{
java.io.InputStreamis=newjava.io.FileInputStream(uploads.get(i));
java.io.OutputStreamos=newjava.io.FileOutputStream(
"d:upload"+fileNames.get(i));
bytebuffer[]=newbyte[8192];
intcount=0;
while((count=is.read(buffer))>0)
{
os.write(buffer,0,count);
}
os.close();
is.close();
}
}
returnSUCCESS;
}
}    在execute方法中,只是对List对象进行枚举,在循环中的代码和上传单个文件时的代码基本相同。如果读者使用过struts1.x的上传组件,是不是感觉Struts2的上传功能更容易实现呢?在Struts1.x中上传多个文件时,可是需要建立带索引的属性的。而在Struts2中,就是这么简单就搞定了。图1是上传任意多个文件的界面。

本文作者:佚名 来源:本站原创
CIO之家 www.ciozj.com 微信公众号:imciow
   
免责声明:本站转载此文章旨在分享信息,不代表对其内容的完全认同。文章来源已尽可能注明,若涉及版权问题,请及时与我们联系,我们将积极配合处理。同时,我们无法对文章内容的真实性、准确性及完整性进行完全保证,对于因文章内容而产生的任何后果,本账号不承担法律责任。转载仅出于传播目的,读者应自行对内容进行核实与判断。请谨慎参考文章信息,一切责任由读者自行承担。
延伸阅读