Struts2转换器、拦截器、验证以及国际化

来源:互联网 发布:怎么在淘宝卖虚拟产品 编辑:程序博客网 时间:2024/04/28 02:06

      Struts2转换器、拦截器、验证以及国际化

目录

l 类型转换概述

l 文件上传

l 拦截器

l 输入校验

l 国际化

                      类型转换

类型转换概述:

Web项目开发中由于很多数据在页面上显示时候都是以字符串类型来显示。而在控制层或者model层中开发人员使用Java开发时候,对于这些从页面上传入或者需要传到页面上显示的数据开发的类型不一定都是字符串类型。因此常常需要在视图和非视图之间进行类型转换。最明显的例子就是显示当前日期。可是这些开发工作往往是无关紧要或者说是犹如“鸡肋”,而很多开发时间却都白白浪费在这上面。有感于此,Struts2的设计者提供了类型转换的功能。类型转换也是用拦截器来实现的。使用Struts2类型转换功能,看看在Struts2中到底是如何实现或者说如何实现类型转换。

Struts2类型转换使用介绍 

l Struts2的类型转换几乎支持Java中各种数据类型的转换。甚至开发者还可以自定义自己的类型转换功能。不过在笔者看来,不推荐开发人员开发自定义的类型转换功能。原因有二。一是遵循IT界著名名言“不重复发明轮子”,不在前人的成果上再次浪费时间。二是类型转换本身在开发工作中就不应该占用大量时间和人力。况且自定义自己的类型转换,项目风险也有可能增加。从项目管理角度对时间、成本、风险的管理都存在负面效应。

l Struts2本身所具有的类型转换功能。大致分为以下:

l intbooleandoubleJava基本类型转换。

l Date类型转换。

l List类型转换。

l Set类型转换。

l 数组类型转换。

l 除了数组的类型转换不大实用以外,其他几种类型转换都是比较常用的。而且Date类型转换也是属于单个Java变量的转换。

自定义类型转换器:

java.util.Date类型的属性可以接收格式为2009-07-20的请求参数值。但如果我们需要接收格式为20091221的请求参数,我们必须定义类型转换器,否则struts2无法自动完成类型转换。

import java.util.Date;

public class HelloWorldAction {

private Date createtime;

public Date getCreatetime() {

return createtime;

}

public void setCreatetime(Date createtime) {

this.createtime = createtime;

}

}

public class DateConverter extends DefaultTypeConverter {

                @Override  

public Object convertValue(Map context, Object value, Class toType) {

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");

try { 

if(toType == Date.class){//当字符串向Date类型转换时

String[] params = (String[]) value;// Request.getParameterValues() 

return dateFormat.parse(params[0]);

}else if(toType == String.class){//Date转换成字符串时

Date date = (Date) value;

return dateFormat.format(date);

}

} catch (ParseException e) {}

return null;

}

}

将上面的类型转换器注册为局部类型转换器:

Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassNameAction的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应为HelloWorldAction-conversion.properties 。在properties文件中的内容为:

属性名称=类型转换器的全类名

对于本例而言, HelloWorldAction-conversion.properties文件中的内容为:

createtime= cn.itcast.conversion.DateConverter

自定义全局类型转换器:

l 将上面的类型转换器注册为全局类型转换器:

l 在WEB-INF/classes下放置xwork-conversion.properties文件 。在properties文件中的内容为:

l 待转换的类型=类型转换器的全类名

l 对于本例而言, xwork-conversion.properties文件中的内容为:

l java.util.Date= cn.itcast.conversion.DateConverter

访问或添加request/session/application属性

public String scope() throws Exception{

   ActionContext ctx = ActionContext.getContext();

   ctx.getApplication().put("app", "应用范围");//ServletContext里放入app

   ctx.getSession().put("ses", "session范围");//session里放入ses

   ctx.put("req", "request范围");//request里放入req

   return "scope";

}

JSP:

 <body>

    ${applicationScope.app} <br>

    ${sessionScope.ses}<br>

    ${requestScope.req}<br>

 </body>

文件上传

第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jarcommons-io-1.3.2.jar。这两个文件可以从http://commons.apache.org/下载。

第二步:把form表的enctype设置为:“multipart/form-data“,如下:

<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">

  <input  type="file" name="uploadImage">

</form>

第三步:在Action类中添加以下属性,属性红色部分对应于表单中文件字段的名称:

public class HelloWorldAction{

  private File uploadImage;//得到上传的文件

  private String uploadImageContentType;//得到文件的类型

  private String uploadImageFileName;//得到文件的名称

  //这里略省了属性的getter/setter方法

  public String upload() throws Exception{

String realpath = ServletActionContext.getServletContext().getRealPath("/images");

File file = new File(realpath);

if(!file.exists()) file.mkdirs();

FileUtils.copyFile(uploadImage, new File(file, uploadImageFileName));

return "success";

  }

}

多文件上传

第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jarcommons-io-1.3.2.jar。这两个文件可以从http://commons.apache.org/下载。

第二步:把form表的enctype设置为:“multipart/form-data“,如下:

<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/xxx.action" method="post">

  <input  type="file" name="uploadImages">

  <input  type="file" name="uploadImages">

</form>

第三步:在Action类中添加以下属性,属性红色部分对应于表单中文件字段的名称:

public class HelloWorldAction{

  private File[] uploadImages;//得到上传的文件

  private String[] uploadImagesContentType;//得到文件的类型

  private String[] uploadImagesFileName;//得到文件的名称

  //这里略省了属性的getter/setter方法

  public String upload() throws Exception{

String realpath = ServletActionContext.getServletContext().getRealPath("/images");

File file = new File(realpath);

if(!file.exists()) file.mkdirs();

for(int i=0 ;i<uploadImages.length; i++){ File uploadImage = uploadImages[i];

    FileUtils.copyFile(uploadImage, new File(file, uploadImagesFileName[i]));

}

return "success";

  }}

                     拦截器

l Struts2另一核心技术是拦截器,英文名为Interceptor。它原来是WebWork框架中一个很好的支持国际化、校验、类型转换的工具。现在WebWorkStruts合并成Struts2之后,理所当然也成为了Struts2的一部分。

l 拦截器本身也是一个普通的Java对象,它的功能是动态拦截Action调用,在Action执行前后执行拦截器本身提供的各种各样的Web项目需求。当然也可以阻止Action的执行,同时也可以提取Action中可以复用的部分。

l 在Struts2中还有个拦截器栈的概念,其实它就是拦截器的一个集合。它把多个拦截器集合起来,按照在栈中配置的顺序执行,特别是针对Action可以拦截相应的方法或者字段。

拦截器示例

public class MyTimerInterceptor extends AbstractInterceptor{

public String intercept(ActionInvocation invocation) 

throws Exception {

//预处理工作

long startTime = System.currentTimeMillis();

              //执行后续拦截器或Action

String result = invocation.invoke();

              //后续处理工作

       long execTime = System.currentTimeMillis() - startTime;

             System.out.println("The interval time is "+execTime+" ms");

//返回结果字符串

              return result;

}

}

Struts 2自带拦截器

Params拦截器 

负责将请求参数设置为Action属性

servletConfig拦截器 

将源于Servlet API的各种对象注入到Action

 fileUpload拦截器

对文件上传提供支持

exception拦截器

捕获异常,并且将异常映射到用户自定义的错误页面

validation拦截器 

调用验证框架进行数据验证 

workflow拦截器

调用Action类的validate(),执行编码验证

配置拦截器

<package name="packName" extends="struts-default" namespace="/manage">

<interceptors>

<!-- 定义拦截器 -->

<interceptor name="interceptorName" class="interceptorClass" />

<!-- 定义拦截器栈 -->

<interceptor-stack name="interceptorStackName">

<!--指定引用的拦截器-->

<interceptor-ref name="interceptorName|interceptorStackName" />

</interceptor-stack>

</interceptors>

<!--定义默认的拦截器引用-->

<default-interceptor-ref name="interceptorName|interceptorStackName" />

<action name="actionName" class="actionClass">

  <!—为Action指定拦截器引用-->

<interceptor-ref name="interceptorName|interceptorStackName" />

<!--省略其他配置-->

</action>

</package>

拦截器在Struts2中的缺省应用 

l 拦截器和拦截器栈的定义配置格式。

l Struts2定义的各个拦截器功能介绍。

l Struts-default.xml文件中的拦截器配置

自定义拦截器

要自定义拦截器需要实现com.opensymphony.xwork2.interceptor.Interceptor接口:

public class PermissionInterceptor implements Interceptor {

   private static final long serialVersionUID = -5178310397732210602L;

   public void destroy() {

   }

   public void init() {

   }

   public String intercept(ActionInvocation invocation) throws Exception {

  System.out.println("进入拦截器");

if(session里存在用户){

String result = invocation.invoke();

}else{

return logon;

}

//System.out.println("返回值:"+ result);

//return result;

    }

}

<package name="itcast" namespace="/test" extends="struts-default">

<interceptors>

           <interceptor name=permission" class="cn.itcast.aop.PermissionInterceptor" />

           <interceptor-stack name="permissionStack">

   <interceptor-ref name="defaultStack" />

  <interceptor-ref name=" permission " />

            </interceptor-stack>

  </interceptors>

<action name="helloworld_*" class="cn.itcast.action.HelloWorldAction" method="{1}">

<result name="success">/WEB-INF/page/hello.jsp</result>

<interceptor-ref name="permissionStack"/>

</action>

</package>

因为struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,所以我们定义的拦截器需要引用系统默认的defaultStack,这样应用才可以使用struts2框架提供的众多功能。

如果希望包下的所有action都使用自定义的拦截器,可以通过<default-interceptor-ref name=permissionStack/>把拦截器定义为默认拦截器。注意:每个包只能指定一个默认拦截器。另外,一旦我们为该包中的某个action显式指定了某个拦截器,则默认拦截器不会起作用。

输入校验

struts2中,我们可以实现对action的所有方法进行校验或者对action的指定方法进行校验。

对于输入校验struts2提供了两种实现方法:

1. 采用手工编写代码实现。

2. 基于XML配置方式实现。

手工编写代码实现对action中所有方法输入校验

通过重写validate() 方法实现, validate()方法会校验action中所有与execute方法签名相同的方法。当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息(为了使用addFieldError()方法,action可以继承ActionSupport ),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为inputresult。在input视图中可以通过<s:fielderror/>显示失败信息。

validate()使用例子:

public void validate() {

       if(this.mobile==null || "".equals(this.mobile.trim())){  this.addFieldError("username", "手机号不能为空");

        }else{  if(!Pattern.compile("^1[358]\\d{9}").matcher(this.mobile.trim()).matches()){

this.addFieldError(mobile", "手机号的格式不正确"); }

       }

}

验证失败后,请求转发至input视图:

<result name="input">/WEB-INF/page/addUser.jsp</result>

addUser.jsp页面中使用<s:fielderror/>显示失败信息。

通过validateXxx()方法实现, validateXxx()只会校验action中方法名为Xxx的方法。其中Xxx的第一个字母要大写。当某个数据校验失败时,我们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息(为了使用addFieldError()方法,action可以继承ActionSupport ),如果系统的fieldErrors包含失败信息,struts2会将请求转发到名为inputresult。在input视图中可以通过<s:fielderror/>显示失败信息。

validateXxx()方法使用例子:

public String add() throws Exception{  return "success";}

public void validateAdd(){

          if(username==null && "".equals(username.trim()))  this.addFieldError("username", "用户名不能为空");

}

验证失败后,请求转发至input视图:

<result name="input">/WEB-INF/page/addUser.jsp</result>

addUser.jsp页面中使用<s:fielderror/>显示失败信息。

输入校验的流程

1。类型转换器对请求参数执行类型转换,并把转换后的值赋给action中的属性。

2。如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContextconversionError拦截器将异常信息添加到fieldErrors里。不管类型转换是否出现异常,都会进入第3步。

3。系统通过反射技术先调用action中的validateXxx()方法,Xxx为方法名。

4。再调用action中的validate()方法。

5。经过上面4步,如果系统中的fieldErrors存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的处理方法。

基于XML配置方式实现对action的所有方法进行输入校验

使用基于XML配置方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件,校验文件和action类放在同一个包下,文件的取名格式为:ActionClassName-validation.xml,其中ActionClassNameaction的简单类名,-validation为固定写法。如果Action类为cn.itcast.UserAction,那么该文件的取名应为:UserAction-validation.xml。下面是校验文件的模版:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.3//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd"> 

<validators>

    <field name="username">

        <field-validator type="requiredstring">

            <param name="trim">true</param>

            <message>用户名不能为空!</message>

        </field-validator>

    </field>

</validators>

<field>指定action中要校验的属性,<field-validator>指定校验器,上面指定的校验器requiredstring是由系统提供的,系统提供了能满足大部分验证需求的校验器,这些校验器的定义可以在xwork-2.x.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到。

<message>为校验失败后的提示信息,如果需要国际化,可以为message指定key属性,key的值为资源文件中的key

在这个校验文件中,对action中字符串类型的username属性进行验证,首先要求调用trim()方法去掉空格,然后判断用户名是否为空。

编写校验文件时,不能出现帮助信息

在编写ActionClassName-validation.xml校验文件时,如果出现不了帮助信息,可以按下面方式解决:

windwos->preferences->myeclipse->files and editors->xml->xmlcatalog

点“add,在出现的窗口中的location中选“File system,然后在xwork-2.1.2解压目录的src\java目录中选择xwork-validator-1.0.3.dtd,回到设置窗口的时候不要急着关闭窗口,应把窗口中的Key Type改为URI Key改为http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd

校验器的使用例子

required  必填校验器

<field-validator type="required">

       <message>性别不能为空!</message>

</field-validator>

requiredstring  必填字符串校验器

<field-validator type="requiredstring">

       <param name="trim">true</param>

       <message>用户名不能为空!</message>

</field-validator>

stringlength:字符串长度校验器

<field-validator type="stringlength">

<param name="maxLength">10</param>

<param name="minLength">2</param>

<param name="trim">true</param>

<message><![CDATA[产品名称应在2-10个字符之间]]></message>

</field-validator>

email:邮件地址校验器

<field-validator type="email">

<message>电子邮件地址无效</message>

</field-validator>

regex:正则表达式校验器

<field-validator type="regex">

     <param name="expression"><![CDATA[^1[358]\d{9}$]]></param>

     <message>手机号格式不正确!</message>

</field-validator>

int:整数校验器

<field-validator type="int">

<param name="min">1</param>

<param name="max">150</param>

<message>年龄必须在1-150之间</message>

</field-validator>

字段OGNL表达式校验器

<field name="imagefile">

<field-validator type="fieldexpression">

<param name="expression"><![CDATA[imagefile.length() <= 0]]></param>

<message>文件不能为空</message>

</field-validator>

</field>

基于XML校验的一些特点

当为某个action提供了ActionClassName-validation.xmlActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:

1AconClassName-validation.xml

2ActionClassName-ActionName-validation.xml

系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到所有校验文件时,会把校验文件里的所有校验规则汇总,然后全部应用于action方法的校验。如果两个校验文件中指定的校验规则冲突,则只使用后面文件中的校验规则。

action继承了另一个action,父类action的校验文件会先被搜索到。

假设UserAction继承BaseAction

<action name="user" class="cn.itcast.action.UserAction" method="{1}">

</action>

访问上面action,系统先搜索父类的校验文件:BaseAction-validation.xml, BaseAction-user-validation.xml,接着搜索子类的校验文件: UserAction-validation.xml, UserAction-user-validation.xml。应用于上面action的校验规则为这四个文件的总和。

国际化和本地化

国际化(InternationalizationI18N

使程序在不做任何修改的情况下,可以在不同国家或地区和不同语言环境下,按照当地的语言和格式习惯显示字符

本地化(LocalizationL10N

一个国际化的程序,当它运行在本地机器时,能够根据本地机器的语言和地区设置显示相应字符

资源文件的范围

全局资源文件 

所有包的所有Action类都可以访问

导致资源文件变得非常庞大臃肿,不便于维护  

包范围资源文件 

对应包下创建package_language_country.properties

处于该包及子包下的action才可以访问该文件

Action范围资源文件

命名格式ActionClassName_language_country.properties 

只有单个Action可以访问

配置全局资源与输出国际化信息

当准备好资源文件之后,我们可以在struts.xml中通过struts.custom.i18n.resources常量把资源文件定义为全局资源文件,如下:

<constant name="struts.custom.i18n.resources" value="itcast" />

itcast为资源文件的基本名。

后面我们就可以在页面或在action中访问国际化信息:

JSP页面中使用<s:text name=“”/>标签输出国际化信息:

<s:text name=user/>name为资源文件中的key

Action类中,可以继承ActionSupport,使用getText()方法得到国际化信息,该方法的第一个参数用于指定资源文件中的key

在表单标签中,通过key属性指定资源文件中的key,如:

<s:textfield name="realname" key="user"/>

国际化—包范围资源文件

在一个大型应用中,整个应用有大量的内容需要实现国际化,如果我们把国际化的内容都放置在全局资源属性文件中,显然会导致资源文件变的过于庞大、臃肿,不便于维护,这个时候我们可以针对不同模块,使用包范围来组织国际化文件。

方法如下:

java的包下放置package_language_country.properties资源文件,package为固定写法,处于该包及子包下的action都可以访问该资源。当查找指定key的消息时,系统会先从package资源文件查找,当找不到对应的key时,才会从常量struts.custom.i18n.resources指定的资源文件中寻找。

国际化—JSP中直接访问某个资源文件

struts2为我们提供了<s:i18n>标签,使用<s:i18n>标签我们可以在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置:

<s:i18n name="itcast">

    <s:text name=welcome/>

</s:i18n>

Itcast为类路径下资源文件的基本名。

如果要访问的资源文件在类路径的某个包下,可以这样访问:

<s:i18n name=cn/itcast/action/package">

   <s:text name="welcome">

   <s:param>小张</s:param>

   </s:text>

</s:i18n>

上面访问cn.itcast.action包下基本名为package的资源文件。