《Struts in Action》第12章翻译,初稿,第一节

翻译 2004年07月30日 23:31:00

Struts In Action 第12章,lamp5w翻译(lamp5w@yahoo.com.cn

Validating user input
(校验用户的输入)

  • 理解“数据校验”的需求
  • 配置和使用Commons Validator
  • 使用多页和本地化校验
  • 编写你自己的Validator

   Us:编程工作在今天成了软件工程师努力建造更大更好的傻瓜式程序和宇宙试图制造更大更多的白痴之间的竞争。目前看来,宇宙是胜利者。
                                               ――Rich Cook

   Them:我从来不知道我说过的话有多少是真实的。
                                               ――Bette Midler

12.1 眼见为实

       大多数的Web应用都需要从用户方收集数据。输入可以通过表单的文本域或者GUI元素例如菜单列表、单选按钮、多选框等。实践表明,用户输入的东西并非总是理性的。某些菜单选项可能是互相排斥的。一个电话号码可能漏掉一个数字,字母可能被输入到数字字段,同样数字也可能被输入到本来希望用户输入字母的字段。这可能是因为登记数据的表单不够聪明或者因为用户不够注意,但是无论如何,事情发生了――而且可能性非常大。


 没有任何事情比用户从应用程序的反馈里得到一堆垃圾更让人沮丧了,就算是因为用户先把垃圾塞给了应用程序。一个严谨的应用程序应该对所有的输入明察秋毫,预先防止一切可以预知的错误,同时也保护了用户本身,毕竟,如果用户把事情弄的一团糟,结果还是要我们来帮他修正。

 

12.1.1 请允许我拒绝你的输入

       在传统的应用程序中,数据录入控件可以简单的把糟糕的数据据之门外,但那是一种奢侈的“模态”。
―――――――――――――――――――――――――――――――――――――-----------
定义:一个用户界面元素,当它要求所有的用户给一个应用程序输入时是“模态的”。其它元素在该元素未被释放之前无法被访问。为了让程序继续运行,用户要么填完整个模态对话框,要么只好关闭应用程序。大多数用户界面元素是“非模态的”。
 ―――――――――――――――――――――――――――――――――――――-----------
 Web应用程序,天生就是“非模态的”,只有少量的选项。缺省情况下,浏览器显示的HTML元素会接受任何的输入,给定的元素对表单之外的输入一无所知。我们可以和JavaScript开个玩笑,但是我们并不能保证用户会在浏览器中允许JavaScript。


 当然,我们可以在数据到达业务逻辑层的时候对它进行校验。(更多关于应用程序“层”和业务对象的内容请参见本书第2章。)许多业务逻辑对象(business logic objects)有内置的数据校验功能,但是大多数业务对象(business objects)在接收数据之前不会对数据进行审查。业务层的方法更趋向于可信任的家伙,它们期望友善的对象能够提供合理的数据并且按照它们所声称的那样工作,即使当业务对象在最保守的情况下,通常它们能做的一切也就是抛出异常而已。进入一个病态的对话和用户一起去改正他的输入不应该是业务对象的责任。
 当然,在上下文环境中校验数据是业务对象的责任,例如,如果一个username和password符合条件。但是,任何时候在数据交换到业务层之前,是有非常多的目标校验规则要实施的。在一个分布式应用中,一个业务对象可能驻留在远端机器上,创建连接往返于客户机和服务器之间来解决简单的数据输入错误将是非常昂贵的代价。

12.1.2 Web层校验
 在实际中,经常进入到web应用框架来提供目标校验任务并且缩小model和view之间的间隙。在非模式中,分布式环境下,我们需要校验任务可以做以下工作:

  • 确保确定的字段有值
  • 确定一个给定的值在期望的模式和范围中
  • 立即检查整个form的数据并且返回一个消息列表
  • 在字段之间比较值
  • 修正行为要返回原始输入
  • 需要的时候要显示本地化消息
  • 在JavaScript被禁用的情况下执行服务端校验

      校验系统另外两个重要的特点是低耦合和可选的客户端校验

      低耦合
作为一个实际问题,输入需要被controller校验,但是业务有效性校验是绑在业务层的。这就意味着校验规则应该分开存储在页面标记和Java代码之间,以让它们可以被复查和修改而不用改变任何其它的源代码。保持校验规则低耦合可以很容易的实现它和业务需求之间的同步。

      ―――――――――――――――――――――――――――――――――――――-----------
定义:耦合的程度涉及两个组件之间的关联强度。耦合是内聚的补充。内聚描述了一个组件的内在内容之间的关系紧密程度,目的是创建具备内在完整性的(强聚合)、小巧的、直接的、可见的、和别的组件保持灵活的关系的组件(低耦合)。〔McConnell〕
―――――――――――――――――――――――――――――――――――――-----------
一些校验规则可能同样需要本地化。当增加对一个新地区的支持的时候,我们应该可以更新校验规则,如同轻易的更新资源绑定。


 当校验规则可能便利的提供给表示层使用时,重要的一点是应该记得它们事实上归属于业务层。校验逻辑不应该和表示层的代码混杂在一起。

 

      客户端校验
客户端校验天生就是不可靠的。哄骗一个web页面提交并且旁路任何原页面的脚本是一件很容易的事情。当我们不能依赖客户端JavaScript校验的时候,它们依然是有用的,它可以立即给用户反馈以避免过多的与服务器交互,节省任何一个人的时间和带宽。所以,另一个理想的特性是从相同的校验规则产生JavaScript和服务端校验。当JavaScript被启用的时候,输入可以在提交之前被客户端所验证,如果JavaScript未被启用,那么输入仍然会被服务端验证以确保不出任何差错。

 

12.1.3 校验推论(Validator consequences)

      使用Jakarta Commons Validator〔ASF,Validator〕得出以下几个推论:

  • Validator是框架组件,可以达到这些要求――甚至更多
  • Validator通过一个XML文件来配置,为表单里的字段产生校验规则
  • Validator定义的规则同样通过XML文件来配置
  • 为基础类型(例如dates、integers)准备的Validator是现成的,如果需要,你可以创建你自己的校验器
  • 正则表达式可以被基于模式的校验使用,例如邮政编码和电话号码
  • 支持多页和本地化校验,你可以为任何语言编写向导

        ―――――――――――――――――――――――――――――――――――――-----------
定义:正则表达式是根据一些模式来匹配字符串的公式。正则表达式应用于诸多的Unix命令行和编程工具。更多关于正则表达式的资料请参考Stephen Ramsay的“Using Regular Expressions”网页。
―――――――――――――――――――――――――――――――――――――-----------
在你的应用中使用Jakarta Commons Validator带来诸多的收益:

  • 资源的最优化使用:当JavaScript被启用的时候,它是可以被提供的,同时服务端的校验也是有保障的。
  • 单点维护:客户端和服务端的校验都是由同一个配置来保障的。
  • 扩展性:客户校验可以通过正则表达式或者Java代码来定义
  • 可维护性:它和应用程序之间是低耦合关系,在维护的时候不需要改变页面标记或者代码
  • 本地化:本地化校验只有在需要的时候被定义
  • 与Struts集成:默认情况下,校验将共享Struts的消息绑定。本地化的文本可以集中并且重用
  • 容易地部署客户端校验:要使用客户端校验,你只需要增加一个单独的JSP标记来生成校验脚本并且通过脚本来提交表单。
  • 容易的配置:Validator是使用一个XML文件来配置的,就像web应用的部署描述和Struts的配置一样

      但是,当然了,它们也还是有缺点的:

  • 非模式的客户端校验:校验器生成的JavaScript是非模式的;它不会干涉用户的输入行为直到表单提交的时候
  • 依赖:校验在字段之间和ActionForm属性之间是分离的。页面标记、ActionForm,Validator,还有Struts配置文件必须同步在一起
  • 缺乏数据的转换和转化:Validator包不提供数据的转换和转化功能,需要的时候,数据的转换和转化必须个别的编程实现。

      请记住,在你的应用中使用Jakarta Commons Validator并不是万灵药,一些校验只能通过服务端来实现。如果服务端校验失败,显示的错误信息是和JavaScript消息不一样的。界面的中止会让用户搞糊涂的。
―――――――――――――――――――――――――――――――――――――-----------
定义:数据转换(Data conversion)是把数据从一种类型转换为另一种类型,例如从String转换为Integer。数据转化(Data transformation)是改变数据的内在格式,例如在一个String显示之前为它追加标点或者在它存储之前去除不需要的标点。本地化可以要求转化数据为一种显示格式。
―――――――――――――――――――――――――――――――――――――-----------
在本章中,我们会展示在你的应用中怎样最佳的使用Commons Validator框架,包括了Validator的全面设计和介绍一个简单的例子。我们一会着眼于深入Validator的每一个组件,连同经常需要(原文:often-needed)的技术,例如最重要的缺省消息、取消校验、使用多页工作流、校验集合等等。


 重点地强调那个目的是很重要的,数据录入校验不是混合的解决方案(原文:omnibus solution),不访问Model的话,许多类型的错误无法被发现。我们可以察看一个username和password是否在长度和成分上符合业务的需求,但是如果想知道一个用户名和密码的组合是否正确,我们就需要到业务层去通过数据服务来校验了。然而,在我们请求(业务层)之前审查数据是否可能正确,我们可以节省昂贵的数据访问事务,这对任何人都有好处。
―――――――――――――――――――――――――――――――――――――
注意:你可能会担心,“那我应该使用Struts框架或者Struts Validator框架来构建我的应用吗?”同时使用,事实上。许多应用是使用多个框架组件的装置来构建的,包括一些开发小组在家里开发的东西。Struts是在Sun的Java J2SE框架框架上建立起来的,同样的,Struts Validator是基于Struts框架建立起来的,所以,就像你的应用可能使用诸多包里的诸多类一样,同样可能使用许多个框架。更多关于使用框架体系来工作的内容,请参考本书第2章。
―――――――――――――――――――――――――――――――――――――
第4章包含了如何随同Struts1.1设置Validator的内容,这一章是开发者如何把Validator实践到你的应用中的向导。

 

12.2 Struts Validator 之概览
 ―――――――――――――――――――――――――――――――――――
 让我们看看Struts Validator是怎样和其它组件互相作用来通过相同的校验规则设置来提供客户端和服务端校验服务的。你可能会惊讶于一旦Validator把所有事项组合到一起之后校验你的数据是多么容易的一件事情。表12.1列出了组成Struts Validator的诸多板块。


表 12.1 主要的Struts Validator组件

组件

描述

Validators(校验器)

处理原生和其它通用类型。基础校验器包括“必须的”(required)、“掩码”(匹配正则表达式)、最小长度、最大长度、范围、原生类型、日期、email和信用卡号码。自定义的(或者插件)校验器同样可以被定义。

Resource bundle(资源捆绑)

提供(本地化的)标签和消息。默认共享Struts消息。

XML configuration file

XML配置文件)

为需要校验的字段定义表单和校验器,校验器可以定义在分开独立的文件里。

JSP tagJSP标签)

为给定的表单名或者action路径生成JavaScript校验器。

ValidatorFrom

自动校验基于form bean名的属性(运行时通过由ActionMapping参数指定的validate方法)。必须被扩展以提供表单所期望的属性

ValidatorActionForm

自动校验基于action路径的属性(运行时通过由ActionMapping参数指定的validate方法)。必须被扩展以提供表单所期望的属性

最初,Commons Validator是作为Struts框架的扩展被创造的,但是自从它可以被使用在框架之外,开发者把它贡献给另外一个Jakarta子项目――Commons。

 Struts发行版包含一个Validator包,包含许多类把Commons Validator和Struts集成在一起。这个包跟随Commons Validator包被扩展,制定了Struts Validator。在本章的balance(译注:glance之笔误也?)中,我们提到Struts Validator是Commons Validator的一个超集。Validator包事实上是几个用Java写的Validator对象的集合。每一个Validator对象执行一条关于另一个对象的属性的规则,在Struts Validator里,这些对象是ActionForm。校验器有标准的入口方法,像Struts Action,用来在需要的时候调用校验器。Validator配置文件让你为表单里面的每个属性关联一个或者多个校验器。


 在实践中,许多应用需要执行许多的校验器。某些字段可能要求输入数据,一个邮政编码缩写词可能总是已知的长度,其它通用的字段类型包括数字、日期、还有信用卡号码。


 其中,Validator装备许多基本校验器来处理这些通用的需要。如果一个基本校验器或者一个正则表达式不能满足你的Validator需要,你可以卷入你自己的Validator并且插入到包里。基本校验器只捆绑它们自己的插件,你的自定义校验器可以做任何基本校验器可以做的工作,或者更多。


 你的应用需要使用的校验器,基本的或者自定义的,可以被指定在一个XML配置文件里面,通常被命名为validation.xml。为了维护工作轻松一些,你可以把关联一个Validator到你的ActionForm属性的规则列入到一个指定的文件里,通常命名为validator-rules.xml。

―――――――――――――――――――――――――――――――――――――
1.0 vs 1.1 在Struts1.0的Validator版本中,使用单个validation.xml文件包含了Validator
定义和表单校验器。Struts1.1让你分离这些组件到独立的文件。在本章中,我们会提到Struts1.1的独立文件的使用。如果你正在使用Struts1.0,所有的配置元素都保留在单个的validation.xml文件里。
―――――――――――――――――――――――――――――――――――――
校验规则文件有一个<form>元素通常和你的Struts应用的<form-bean>元素相应。<form>元素依次有<field>子元素。每一个<field>元素可以指定它必须通过一个或者多个校验器的校验才成功。如果一个校验器失败,它可以反馈一个跟随着任何代替参数的关键字到一个应用程序资源里的消息模板,Struts使用那个关键字和参数来生成一个本地化的错误消息。如果客户端校验可以使用,相同的消息可以显示在JavaScript窗口里,如图12.1。


 校验文件是你插入校验规则需要的基本校验器或者自定义校验器的地方,在这里你可以指定使用哪一个Validation类,跟随可选的客户端JavaScript。使用的时候,JavaScript校验必须在表单提交到应用程序之前通过。


 在校验器配置文件的JavaScript元素旁边,另一块客户端的校验是<validator:javascript>标记(Struts1.0)或者<html:javascript>标记(Struts1.1)。如果你在JSP页面的任何地方放置这个标记,它将联合所有校验器的JavaScript到一个单独的脚本以期可以被通过一个标准的入口方法调用,然后你可以使用<html:form>标记里的onsubmit属性来调用所有的入口方法。如果JavaScript校验通过了,表单将会被提交,否则,一个弹出式窗口会输出本地化的错误消息。


 为启用服务端校验,你可以简单的从一个Validator包里的基类扩展你的ActionForms。ValidatorForm(org.apache.struts.validator.ValidatorForm)类对应标准的ActionForm。ValidatorDynaform类(org.apache.struts.validator.DynaValidatorForm)对应DynaActionForm。


 缺省情况下,Validator<form>元素使用属性或者formbean名称匹配到ActionForm。二者选一,你可以使用ValidatorForm(org.apache.struts.validator.ValidatorForm)或者ValidatorDynaForm(org.apache.struts.validator.DynaValidatorForm)来匹配<form>元素,这一匹配使用ActionMapping路径来实现。

图12。1 一个Struts Validator生成的JavaScript消息窗口

下一节中,我们将用一个登录应用程序作为客户端和服务端校验的背景向你展示怎样把所有这些东西放到一起。
―――――――――――――――――――――――――――――――――――――――
2.0 vs 1.1 在1.1的发行版本中,Struts Validator 被捆绑在Struts JAR文件里,并且安排
为正式发行版的一个可选组件。重要的和一些次要的实现细节上有所改变,但是包在本质上是没有改变的。实现上的不同,我们会为每一个发行版本展示一个单独的列表。
―――――――――――――――――――――――――――――――――――――――

12.2.2 登录范例
 配合第3章的Struts Validator登录程序将帮助我们阐明这些组件是如何融洽相处在一起的。然后,在12.3节中,我们将深入展开每一个组件。


 在你设置好相关的包之后(参见第4章),你的下一步是确定那些你需要的校验器在你的手上。这些校验器可以保存在一个单独的文件里,默认的被命名为validator-rules.xml。我们的例子只用到required校验器,虽然绝大多数应用还会用到许多其它的校验器。


 接下来的内容中,我们会看到我们的需求,使用Struts Validator来校验一对用户名和密码。

Validator-rules.xml
Struts Validator 分发版包含原生类型的校验器和其它常用的需求,像e-mail和信用卡校验。在实践中,一般来说,那些基本校验器已经可以满足你所有的需要,如果不是,你可以编写你自己的校验器并且在validator-rules.xml文件中和基本校验器一起定义它们(要注意的是,在Struts1.0中,校验器和表单校验都定义在单个的validation.xml文件中)。


 清单12.1和12.2将罗列关于Struts1.1下required校验器的Java和XML源代码,更多关于编写你自己的插件式的校验器的内容,参见12.9节。

清单12.1 required校验器的XML源码(Struts1.0)
<validator name="required"
<!-- ① -->
classname="org.apache.struts.util.StrutsValidator"
method="validateRequired"
<!-- ② -->
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionErrors,
javax.servlet.http.HttpServletRequest"
msg="errors.required">
<!-- ③ -->
<javascript><![CDATA[
function validateRequired(form) {
var bValid = true;
var focusField = null;
var i = 0;
var fields = new Array();
oRequired = new required();
for (x in oRequired) {
if ((form[oRequired[x][0]].type == 'text' ||
form[oRequired[x][0]].type == 'textarea' ||
form[oRequired[x][0]].type == 'select-one' ||
form[oRequired[x][0]].type == 'radio' ||
form[oRequired[x][0]].type == 'password') &&
form[oRequired[x][0]].value == '') {
if (i == 0)
focusField = form[oRequired[x][0]];
fields[i++] = oRequired[x][1];
bValid = false;
}
}
if (fields.length 0) {
focusField.focus();
alert(fields.join('/n'));
}
return bValid;
}
]]>
</javascript>
</validator>
 ―――――――――――――――――――――――――――――――――――――
①本节包含对服务端validator的引用(参见清单12.2)。
②methodParams只在Struts1.1中使用
③客户端JavaScript被包含在XML元素里

像我们看到的,Validator元素被定义在两部分里:

  • 为服务端校验用的Java类和方法
  • 为客户端校验用的JavaScript


required校验器调用的Java方法显示在清单12.2里面。


清单12.2  validateRequired方法的Java代码(Struts1.1)
 public static boolean validateRequired(Object bean,
   ValidatorAction va, Field field,
ActionErrors errors,HttpServletRequest request) {
String value = null;
if (isString(bean)) {
value = (String) bean;
}
else {
value = ValidatorUtil.getValueAsString(
bean, field.getProperty());
}
if (GenericValidator.isBlankOrNull(value)) {
errors.add(field.getKey(),
StrutsValidatorUtil.getActionError(
request, va, field));
return false;
} else {
return true;
}
}
―――――――――――――――――――――――――――――――――――――

application.properties
如果一个校验器失败,它返回一个可以和标准资源捆绑一起被使用的消息关键字和替代参数。默认的,Struts Validator共享被你的应用的其余部分使用的资源绑定,通常命名为ApplicationResources.properties,或者干脆为application.properties。当另一个消息没有被指定,Struts Validator将自动地在缺省资源绑定中查找消息。通常为消息创建一个关键字,用"."(点号)和校验器名称连接errors, required校验器条目看起来就是下面的样子:
                  errors.required={0} is required 。
当我们配置一个使用required校验器的字段,我们同样作为可替换的参数传递字段的标签。然后校验器可以为所有的required字段重用同样的消息。二者选一,你可以定义你自己的消息来选择性的覆盖(override)默认的消息(参见12.4.3节)。


 资源绑定将包含任何你的应用程序需要的其它消息,连同Struts Validator指定的标签和Struts Validator需要的消息。下面是一块我们的"username"和"login"校验所需要的东西:
 # -- logon --
logon.username.maskmsg=Username must be letters and numbers, no spaces.
logon.password.maskmsg=Password must be five characters long and contain a
special character or numeral.


我们同样需要用于"username"和"password"字段的标签,然而,如果应用程序已经被本地化的话,它们就已经被提供了:
 logon.username.displayname=Username
logon.password.displayname=Password


注意,我们使用"logon"作为标签和消息的前缀,给每个form一个自己的命名空间,可以避免应用增长(扩展)所带来的冲突。

 

Validator.xml
当在validator.xml文件里面定义我们的"formset"元素的时候校验器和消息关键字被使用,如清单12.3所罗列。


清单12.3 一个formset元素

<!-- 1 -->

<formset>

<!-- 2 -->

<form name="logonForm">

<!-- 3,4 -->

<field property="username" depends="required,mask">

<!-- 5 -->

<msg

name="mask"

key="logon.username.maskmsg"/>

<!-- 6 -->

<arg0 key="logon.username.displayname"/>

<!-- 7,8 -->

<var>

<var-name>mask</var-name>

<var-value>^[a-zA-Z0-9]*$</var-value>

</var>

</field>

<!-- 9 -->

<field

property="password"

depends="required,minlength">

<arg0 key="logon.password.displayname"/>

<var>

<var-name>minlength</var-name>

<var-value>5</var-value>

</var>

</field>

</form>

<!-- ... -->

</formset>

―――――――――――――――――――――――――――――――――――――

1一个“formset”是一个或多个form的包装。

2每一个form元素被赋予一个自有的名称(name),这将符合你的Struts Configuration中的formbean名或者Action路径。

3每个form元素由许多的field元素组成。

4field元素和depends属性指定了具体要使用何种校验器

5可选的msg元素让你为一个校验器指定一个自定义消息关键字和可以为任何可替代的参

数使用的消息关键字

6arg0元素指定第一个可替代的参数,任何需要它们的消息都可以使用它们

7var元素用来传递变量属性到校验器

8这里我们传递一个正则表达式到“mask”校验器,表达式说,usernames只能包含字母表里面的字符和数字。

9在这里我们说一个密码是必须的并且长度必须大于5个字符,密码长度是一个业务需求,

可以让账号更加安全。密码校验消息使用缺省的“minlength”或者“required”消息,这些消息在validation-rules.xml文件里面定义(errors.minlength和errors.required)。

 

JSP 标记/logon.jsp

JavaSctipt校验是可选的,但是可以轻易的实现如果你想在你的应用程序中使用它们。清单12.4展示了为显示JavaScript校验而改进的logon.jsp。

 

清单 12.4 logon.jsp 为JavaScript校验而准备

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

<!-- 1 -->

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

<HTML><HEAD><TITLE>Sign in, Please!</TITLE></HEAD>

<BODY>

<!-- 2 -->

<html:form action="/logonSubmit" focus="username"

onsubmit="validateLogonForm(this)">

<TABLE border="0" width="100%">

<TR><TH align="right">Username:</TH>

<TD align="left"><html:text property="username"/></TD>

</TR>

<TR><th align="right">Password:</TH>

<TD align="left"><html:password property="password"/></TD>

</TR>

<TR>

<TD align="right"><html:submit property="submit" value="Submit"/></TD>

<TD align="left"><html:reset/></TD>

</TR>

</TABLE

</html:form>

<!-- 3 -->

<validator:javascript formName="logonForm"/>

</BODY>

</HTML>

――――――――――――――――――――――――――――――――――――――――――

1这里我们导入校验器的标签库

2这部分调用校验器脚本,然后,表单被提交

3这里我们增加一个标签来在页面任何位置输出JavaScript校验方法(validate method)

为启用服务端校验,所有要做的一切只是让formbean继承ValidatorForm来代替继承ActionForm

Public final class LogonForm extends

org.apache.struts.validator.action.ValidatorForm{}

并且去除原来的validate方法。当控制器(controller)调用validate方法的时候,ValidatorForm方法将踢开validate方法而遵行我们在validation.xml文件定义的规则。

 

12.3 基础校验器

   ―――――――――――――――――――――――――――――――――――――

   如表12.2所示,Struts Validator附带14个基本校验器。这些校验器基本上涵盖了大多数应用的需要,如果有其它需要产生,像我们在12.9节看到的,你可以添加自定义的或者插件形式的校验器。

   表12.2 基本校验器                 

  

校验器

用途

required

成功条件:字段包含除空白之外的任何字符

mask

成功条件:被校验的值匹配mask属性给出的正则表达式

range

成功条件:被校验的值在min和max属性指定的范围之内

((value >= min) & (value <= max))

maxLength

成功条件:字段的长度小于或等于max属性

minLength

成功条件:字段的长度大于或等于max属性

byte,short,integer,

long,float,double

成功条件:被校验的值可以被转换为相应的原生类型

date

成功条件:被校验的值描述了一个有效的日期,可能需要提供日期式样

reditcard

成功条件:被校验的值是一个有效的信用卡号码

email

成功条件:被校验的值是一个有效的email地址

12.3.1 required 校验器

   required校验器是最简单也最常用的校验器:

       <field

           property="customerId"

           depends="required"/>

   如果没有任何东西或者只有空白输入到一个字段,将导致这个校验器失败,同时一个error被传递回来到framework,否则,校验器成功。为测定是否一个字段只包含空白,标准的String.trim()方法被调用(value.trim().length()==0)。

   因为浏览器不会提交空字段,任何非required字段将跳过所有校验器如果字段是空或者长度为零。

12.3.2 mask(掩码)校验器

   mask校验器检查和正则表达式相反的值,如果模式匹配,校验器成功。

       <field property="postalCode" depends="mask">

<arg0 key="registrationForm.postalCode.displayname"/>

<var>

<var-name>mask</var-name>

<var-value>^/d{5}/d*$</var-value>

</var>

</field>

Jakarta RegExp包是用来解析正则表达式的。如果一个表达式需要被超过一个字段使用,它同样可以在validation.xml文件里被定义为一个常量(constant),例如:

   <constant>

<constant-name>zip</constant-name>

<constant-value>^/d{5}/d*$</constant-value>

</constant>

   像大多数其它标准校验器,mask校验器是声明为依赖required校验器的,因此,如果一个字段同时依赖required和mask,required校验器必须在mask校验器应用之前成功完成。

12.3.3 range(范围)校验器

   range校验器检查一个值是否在指定的最大值和最小值的范围之间:

       <field property="priority"

   depends="required,integer,range">

   <arg0 key="responseForm.priority.displayname"/>

   <var>

       <var-name>min</var-name>

       <var-value>1</var-value>

   </var>

   <var>

       <var-name>max</var-name>

       <var-value>4</var-value>

   </var>

</field>

   这个校验器将会成功在字段中被输入数字1、2、3、或4的情况下,在实践中,错误消息将显示范围的最大值和最小值来帮助用户来正确输入。你可以使用arg元素来在消息中包含min和max变量的引用:

       <field property="priority"

depends="required,integer,range">

<arg0 key="responseForm.priority.displayname"/>

<arg1 name="range" key="${var:min}" resource="false"/>

<arg2 name="range" key="${var:max}" resource="false"/>

<var>

<var-name>min</var-name>

<var-value>1</var-value>

</var>

<var>

<var-name>max</var-name>

<var-value>4</var-value>

</var>

</field>

   这就暗示着范围消息的模板看起来就像下面的东西:

       errors.range=Please enter a value between {1} and {2}.

   如果range校验器在优先字段失败,会看到以下校验消息:

       Please enter a value between 1 and 4.

   缺省的,校验器假定arg元素的关键字匹配一个在资源捆绑的关键字,同时将用资源条目的值来代替key属性的值。(原文:By default,the validator assumes that the key of an arg element mat hes a key in the resource bundle and will substitute the value of the resource entry for the value of the key attribute.)resource=false 开关告诉校验器不作修改的使用value原来的样子。如果values要被一个sele t元素加工,你可能希望在sele t列表上映射消息到first和last项:

       <arg1 name="range" key="priority.range.first"/>

<arg2 name="range" key="priority.range.last"/>

这意味着在某个地方还有如下的条目:

       priority.range.first=do-it-now

priority.range.last=forget-about-it

在资源捆绑(resource bundle)里面,如果一个校验失败,将可以看到优先的消息:

   Please enter a value between do-it-now and forget-about it.

12.3.4 maxLength(最大长度)校验器

maxLength校验器检查一个范围的高端,如果字段的长度小于或等于max属性,它将成功:

   <field property="remarks"

      depends="maxlength">

<arg0 key="responseForm.remarks.displayname"/>

<arg1 name="maxlength" key="${var:maxlength}"

         resource="false"/>

<var>

     <var-name>maxlength</var-name>

     <var-value>1000</var-value>

</var>

</field>

   这个字段元素保证remarks(可能是一个文本域)的长度不超过1000个字符。注意,我们把长度作为一个参数传递给校验消息,就像我们在range校验器里的做法。

12.3.5 minLength(最小长度)校验器

   minLength校验器检查一个范围的低端,如果字段的长度大于或等于min属性,它将成功:

       <field property="password"

          depends="required,minLength">

          <arg0 key="logon.password.displayname"/>

          <arg1 name="minlength" key="${var:minlength}"

                 resource="false"/>

          <var>

             <var-name>minlength</var-name>

             <var-value>5</var-value>

          </var>

</field>

   field元素规定password必须被输入并且长度必须超过五个字符。

12.3.6 byte,short,integer,long,float,double校验器

这些校验器都对value应用标准的type.parseType方法。如果一个异常被捕捉,校验器返回false,否则,校验器成功。

<field property="amount"

depends="required,double">

<arg0 key="typeForm.amount.displayname"/>

</field>

12.3.7 date校验器

   date校验器检查看value是否描述一个正确的日期:

<field property="date"

depends="required,date">

<arg0 key="typeForm.date.displayname"/>

</field>

   校验器传递框架维护的标准的locale对象(java.util.locale)到日期工具,所以result被自动的本地化。datePattern属性会传递一个标准的日期式样到一个java.text.SimpleDateFormat对象。

<var>

<var-name>datePattern</var-name>

<var-value>MM/dd/yyyy</var-value>

</var>

   在内部,datePattern属性是在SimpleDateFormat构造器被被使用的,然后被用来解析values:

   SimpleDateFormat formatter = new SimpleDateFormat(datePattern);

Date date = formatter.parse(value);

如果解析成功,校验器也就成功。

   如果datePatternStri t属性被设置来代替,长度也会被检查来保证一个首位的零在适当的情况下被包含:

<var>

<var-name>datePatternStri t</var-name>

<var-value>MM/dd/yyyy</var-value>

</var>

   在没有指定样式的情况下,适合用户地区的DateFormat.SHORT将被使用。如果Struts locale对象不可用,服务器的缺省locale将被使用。setLenient方法对于所有的日期转化中设置为false。

12.3.8 reditcard(信用卡号码)校验器

   reditcard校验器分析value看是否是一个信用卡号码:

<field property=" reditcard"

depends="required, reditcard">

<arg0 key="typeForm. reditcard.displayname"/>

</field>

   信用卡号码包含一个奇偶校验数字,validation检查这个数字和其它业务规则,像卡的前缀是否匹配一个信用卡提供商(American Express,Dis over,Master ard,VISA)和是否信用卡号码的长度是否和指明的提供商一致。

12.3.9 email校验器

   email校验器使用一个extensive检查一个预期的 email地址的格式来确保它和发行的规范一致:

       <field property="email"

depends="required,email">

<arg0 key="typeForm.email.displayname"/>

</field>

12.4 资源捆绑(Resource bundles

―――――――――――――――――――――――――――――――――――――

校验器的根本用途就是使得用户修正输入并重试。因为那个过程包括显示字段标签和消息,Struts Validator充分利用Java的本地化特性和框架支持的那些特性。(Java本地化特性包含在13章里面。)

当然,框架同样也需要为字段提供本地化标签的支持。因为Struts资源捆绑是作为应用级资源被提供的,Validator可以在框架中分享相同的绑定,所以你可以把你的标签和消息集中起来。你只不过需要为你使用的校验器和任何当校验一个特殊字段时所需要的自定义消息增加缺省的消息。

12.4.1 缺省捆绑(default bundle

   Struts bundle是通过部署描述(参见第四章)配置的。它通常命名为ApplicationResources.properties或者仅仅application.properties。在我们的案例应用程序中,bundles存储在/WEB-INF/sr /java/resources包下面。我们的缺省Properties文件(java.util.Properties)命名为application.properties。为支持本地化的文件然后被命名为application_en.properties,application_es.properties,application_fr.properties,等等。(再次,更多关于本地化的内容请参见13章。)

   有Validator在,你不需要作任何特别的事情去开始使用Struts资源捆绑,仅仅添加你将需要的任何消息或者标签然后提交到Validator的配置文件里的资源关键字。如果应用程序将要被本地化,适合字段标签的关键字应该已经存在,你可以在框架的其它部分共享它们,如下所示:

       <field property="lastName"

              depends="required">

          <arg0 key="registrationForm.lastName.displayname"/>

</field>

12.4.2 缺省校验器消息(validator messages

   如果一个自定义消息不是被字段元素所提供,当校验器失败的时候缺省消息将被使用。校验器的缺省消息的关键字在它被定义的时候是确定的。惯例是添加一个errors,把校验器名作为前缀。适合required校验器的缺省消息于是成为errors.required。

   这个惯例与查找errors.header和errors.footer条目的Struts<html:error>标记吻合。清单12.5展示了你可以为基本校验器添加到一个英文资源捆绑的关键字和模板。

   清单12.5 适合于基本校验器的缺省消息         

   # Struts Validator Basic Error Messages

errors.required={0} is required.

errors.minlength={0} annot be less than {1} characters.

errors.maxlength={0} annot be greater than {1} characters.

errors.invalid={0} is invalid.

errors.byte={0} must be a byte.

errors.short={0} must be a short.

errors.integer={0} must be an integer.

errors.long={0} must be a long.

errors.float={0} must be a float.

errors.double={0} must be a double.

errors.date={0} is not a date.

errors.range={0} is not in the range {1} through {2}.

errors. reditcard={0} is not a valid reditcard number.

errors.email={0} is not a valid e-mail address.

―――――――――――――――――――――――――――――――――――――

你可能注意到,那里没有适合errors.mask的条目,出于历史原因,适合于mask校验器的定义指定errors.invalid来代替errors.mask。

   每一个校验器消息可以占据四个替代者参数,它们由字段定义里面的arg元素指定。第一个参数,arg0或者{0},通常是字段标签的关键字。校验器将从资源捆绑为标签查找显示文本,同时合并(merge)本地化文本到消息模板。

12.4.3 自定义校验器消息

   一个字段同样可以指定一个自定义的校验器消息来代替缺省的消息。这种现象通常发生在mask校验器将被使用的情况下,所以你可以解释正则表达式期望什么模式。消息的关键字在msg元素里面给出,因为不止一个校验器将被一个字段使用,校验器名被包含为msg元素的name属性:

           <field

property="username"

depends="required,mask">

<msg

name="mask"

key="logon.username.maskmsg"/>

<arg0

key="logon.username.displayname"/>

<var>

<var-name>mask</var-name>

<var-value>^[a-zA-Z0-9]*$</var-value>

</var>

</field>

   在Properties文件里,我们可以像下面的描述一样布置一个key/template:

       logon.username.maskmsg={0} must be letters and numbers,no spaces.

12.5 配置文件

   ―――――――――――――――――――――――――――――――――――――

   Struts Validator的强势是,校验是在应用程序源代码之外使用一个XML配置文件的形式声明的。配置指定诸如表单里面的哪一个字段需要校验、字段需要何种校验器、和其它字段要使用的任何特殊设置。交替的formsets可以被配置为不同的场所,同时可以覆盖任何场所敏感的校验。(原文:Alternate formsets can be configured for different locales and override any locale-sensitive validations.)

   所有的框架使用的校验器都是通过XML来配置的,包含pa kage里面附带的基本校验器。你可以省略在你的应用中不需要的校验器,同时可以插入你自己的自定义校验器来让框架附带的东西靠边站。

   这有利于创建一个灵活的包,但是组合所有这些配置到一个单独的文件会导致一个冗长的文档出现。如表12.3所示,Struts Validator事实上可以使用两个XML文件:一个用来设置校验器,另一个包含你的应用的设置。(注意:Struts1.0使用单个的配置文件。)

   表12.3 Struts Validator配置文件                 

  

文件名

描述

Validator-rules.xml

校验器的配置文件

Validator.xml

应用程序的校验(validations)

这有助于一个便利的安排,这么一来,可以容易的在应用之间拷贝一个校验规则的标准设置,然后定制validation.xml文件,或者,如果你喜欢,你仍然可以组合所有的元素到一个单独的validation.xml文件里面。

   在Struts1.1里面,Validator配置文件的路径在struts-config.xml文件里面声明,如下所示:

<plug-in

lassName="org.apache.struts.validator.ValidatorPlugIn">

<set-property

property="pathnames"

value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>

</plug-in>

参见第4章,可以获得更多的关于安装Struts Validator1.1组件到你的应用的消息。

12.6 Validator JSP Tags

   ―――――――――――――――――――――――――――――――――――――

   Struts Validator框架联合(可选的)需要用来校验一个表单的JavaScript到一个单独的脚本。你可以通过一个自定义标记插入脚本到你的JSP页面里,脚本将被调用在表单被提交的时候。如果校验失败,脚本显示一个错误消息窗口,像前面的图12.1所示,同时提交失败。

   另外,提交成功同时控制权转交到ActionForm的validate方法,这就确保校验被触发即使JavaScript不可用。

   在清单12.4中,我们引入<JavaScript>标记,简单的,这个清单保留了原始的<html:errors>标记。原始的<errors>标记可以相当容易的用在页面上但是要求你混合标记和消息,并让它们和好相处。另外,如果消息以块的形式呈现的话,它们都会一起跑到一个单独的段落里。

   在Struts Validator框架下,错误消息为JavaScript校验所共享。实践中,在两者的场合同时使用相同的标记是有问题的。一个更好的办法是,在周围使用简单的消息。(原文:(In practice,using the same markup in both ases is problemati . A much better way to go would be to use plain messages all aroun .)

   Struts Validator标记库提供额外的标记来帮助解决这个非常的问题,<errorsPesent>或<messagesPresent>标记报告是否有任何未决的消息。<message>标记像一个iterator一样工作,所以你的页面可以循环遍历整个对列,倘若沿途有任何需要的标记条目。(原文:..so your page an loop through the queue,providing any makup entries needed along the way.)

   清单12.6重新展示了清单12.4的代码,这次装备了消息标记,清单12.7展示了Struts1.1版本下的情况。

清单12.6 包含校验和附加JSP标记的登录页面(Struts1.0)      

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

<!-- u -->

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

<HTML><HEAD><TITLE>Sign in, Please!</TITLE></HEAD>

<BODY>

<!-- ? -->

<validator:errorsPresent>

<UL>

<!-- ? -->

<validator:errors id="error">

<LI><bean:write name="error"/></LI>

</validator:errors>

</UL>

</validator:errorsPresent>

<!-- ? -->

<html:form action="/logonSubmit" focus="username"

onsubmit="validateLogonForm(this)">

<TABLE border="0" width="100%">

<TR><TH align="right">Username:</th>

<TD align="left"><html:text property="username"/></TD>

</TR>

<TR><th align="right">Password:</TH>

<TD align="left"><html:password property="password"/></TD>

</TR>

<TR>

<TD align="right"><html:submit property="submit" value="Submit"/></TD>

<TD align="left"><html:reset/></TD>

<!-- ? -->

<TD align="left"><html:cancel onclick="bcancel=true"/></TD>

</TR>

</TABLE>

</html:form>

<!-- ‘ -->

<validator:JavaScript formName="logonForm"/>

</BODY>

</HTML>

清单12.6 包含校验和附加JSP标记的登录页面(Struts1.0)      

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

<!-- u -->

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

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

<HTML><HEAD><TITLE>Sign in, Please!</TITLE></HEAD>

<BODY>

<!-- ? -->

<logic:messagesPresent>

<UL>

<!-- ? -->

<logic:messages id="error"

<LI><bean:write name="error"/></LI>

</validator:messages>

</UL>

</validator:messagesPresent>

<!-- ? -->

<html:form action="/logonSubmit" focus="username"

onsubmit="validateLogonForm(this)">

<TABLE border="0" width="100%">

<TR><TH align="right">Username:</th>

<TD align="left"><html:text property="username"/></TD>

</TR>

<TR><th align="right">Password:</TH>

<TD align="left"><html:password property="password"/></TD>

</TR>

<TR>

<TD align="right"><html:submit property="submit" value="Submit"/></TD>

<TD align="left"><html:reset/></TD>

<!-- ? -->

<TD align="left"><html:cancel onclick="bcancel=true"/></TD>

</TR>

</TABLE

</html:form>

<!-- ‘-->

<validator:JavaScript formName="logonForm"/>

</BODY>

</HTML>

―――――――――――――――――――――――――――――――――――――

这里有一些对清单12.6和12.7都要注意的地方:

u因为我们要使用Struts Validator标记,所以我们需要导入标记库。在Struts1.1

   中,我们同样需要导入logic 标记库,因为现在它包含我们可以在这里使用的标记。

?Struts1.0的error标记,或者Struts1.1的message标记,让我们只留HTML在

   页面上而把错误消息留在外面。如果那里没有消息,这个条目区域将被跳过。

?<errors>或<messages>标记像一个iterator一样工作。这些标记向外暴露每一

   条id errors下的消息,以让bean标记可以轮流(在页面上)写出它们。

?一个JavaScript onsubmit事件被添加到<html:form>标记。这将调用我们的校

验器JavaScript当任何提交按钮被按下或者JavaScript submit事件被触发的时候

?为允许用户取消提交行为并且旁路校验,一个JavaScript标志可以被设置。如果提

交按钮的bCancel属性被设置为true,Struts Validator将直接将控制权转交到Action。

‘最后,但不是微不足道的,是目前的<JavaScript>标记。在运行时,它将被代替

为一个script结合那些JavaScript校验器在当前的form里面。那个script,像ActionForm,被命名在action-mapping属性名之后,在这个案例里面是logonForm。

   框架提供的基本校验器都包含JavaScript校验,如果一个可插入的校验器(12.9节)提供一个JavaScript校验,它最好也被包含在这里。要让脚本成功,所有校验都必须成功。

被产生的JavaScript遵循ValidatorForm的page属性,同时将仅仅为字段产生有一个页面数字(page number)小于或者等于form的页面数字的校验器。(原文:The generated JavaScript observes the page property of the ValidatorForm and will only generate validators for fields with a page number equal to or less than the form’s page number.)缺省的页面数字两者都是0。这在多页面向导中是很有用的。(参见12.10.1节)

12.7 ValidatorFormValidatorActionForm

   ―――――――――――――――――――――――――――――――――――――

   要在Struts1.1中启用Struts Validator,仅仅需要遵循第4章的初始化设置说明书,并且从ValidatorForm和ValidatorActionForm继承你的ActionForm。ValidatorForm将根据formbean名匹配formset名。ValidatorActionForm将根据action-mapping路径匹配formset名。

   大多数案例中,Struts Validator可以完全取代那种在ActionForm中编写validate方法的需要。然而,你仍然应该需要一个validate方法,它可以轻易的在validation框架之外工作。在大多数案例中,你可能需要首先调用validator框架,如果这些校验通过,然后可以运行你自己的校验。清单12.8会告诉你这个工作是怎样完成的。

   清单12.8 带校验的登录页面                

   public ActionErrors validate(

       ActionMapping mapping,

       HttpServletRequest request) {

       // u

       ActionErrors errors = super.validate(mapping, request);

       // ?

       if (errors==null) errors = new ActionErrors();

      if (errors.empty()) {

       // ?

           if (notGood(mapping,request))

             errors.add(ActionErrors.GLOBAL_ERROR,

                 new ActionError(

"errors.notGood","goodProperty"));

       }

       if (errors.empty()) return null;

       return errors;

       return (errors);

}

―――――――――――――――――――――――――――――――――――――

Œ首先,我们调用祖先validate方法,在这个案例中,我们调用Validator框架

?因为祖先方法可能返回null,我们首先需要检查null。因为我们要运行我们自己的

validations,如果一个“不是”返回,可能需要发出errors,所以我们创建一个新的ActionErrors对象。如果祖先返回errors,这个实现并不运行我们的validations,虽然如果合适的话你可以就像轻易地执行你自己的校验。

?我们的样例validation调用一个叫做notGood的helper方法,返回我们自定义的

校验的结果。我们从validate方法传递参数来确信notGood知道所有validate所知道的事情。

12.8 本地化校验

   ―――――――――――――――――――――――――――――――――――――

   Validator配置文件包含一个formset元素,formsets是一个共享了公共地区设定的表单的集合的包装。某些表单的某些字段确实需要本地化,通常大多数不需要。Struts Validator允许你本地化所选择的字段并且对剩下的字段使用缺省校验。缺省的formset忽略语言、国家和不同的属性。本地化完全的限制范围,你可以定义一种格式来覆盖仅仅语言,或者仅仅国家,或者在需要的时候覆盖两者。更多关于国际化你的应用的内容,请参见13章。

12.9 可插入的校验器

12.9.1 创建可插入的校验器

12.10 技巧

   ―――――――――――――――――――――――――――――――――――――

  

12.10.1 多页校验

12.10.2 cancel按钮

12.10.3 自定义消息

12.10.4 相关的字段(Interrelated field

12.10.5 validate方法组合校验器

ombining validators with the validate method

12.11 移植一个应用到Struts Validator

12.11.1 设置Validator框架

12.11.2 测试缺省配置

12.11.3 审阅你的校验(Reviewing your validations

12.11.4 扩展ValidatorFormS affold BaseForm

12.11.5 选择性的移植一个校验

12.11.6 增加formset,form,和字段元素

12.11.7 增加新条目到ApplicationResources

12.11.8 呼叫Struts Validator

12.11.9 测试和重复(Test and repeat

12.11.10 移除ActionForm子类

12.12 内容概要

【Spring】【笔记】《Spring In Action》第5章 数据库处理

5.1 Spring数据访问原理      DAO 数据访问对象(data access object)。      DAO提供了数据读取和写入到数据库中的一种方式。他们应该以接口的方式发布功能,...
  • KEY0323
  • KEY0323
  • 2016年06月03日 12:01
  • 7567

Netty In Action中文版 - 第三章:Netty核心概念

注:本篇内容出自《Netty In Action》一书;         注:本人原创译文,转载请注明出处!...
  • abc_key
  • abc_key
  • 2014年07月12日 11:34
  • 11592

Netty in Action (七) 第三章节 Netty组件和设计

这个章节包括: 1)Netty的架构设计和技术点 2)Channel,EventLoop和ChannelFuture 3)ChannelHandler 和 ChannelPipeline 4)Boot...
  • linuu
  • linuu
  • 2016年04月15日 09:35
  • 6737

【Struts框架】第一节Action-模块包含和defaultAction

1.模块包含: struts.xml: 里面可以这么写 说明在struts.xml包含了一个login.xml文件 login.xml: ...
  • u013517797
  • u013517797
  • 2015年03月19日 20:00
  • 878

【Struts框架】第一节Action-简单的数据校验

假设要验证账号密码的正确性,把账号密码传进Action后,Action验证完毕后,如何将验证后的结果返回给静态界面(即前台)呢?Action里面又没有Servlet中的request和response...
  • u013517797
  • u013517797
  • 2015年03月13日 11:24
  • 883

struts in action(铁手翻译)

  • 2007年07月07日 22:13
  • 7.47MB
  • 下载

【struts框架】第一节Action-struts基础

struts本质:把请求与最后的结果分开(MVC) struts的基本环境: a.需要的jar包 commons-fileupload-1.2.1.jar commons-io-1.3.2.j...
  • u013517797
  • u013517797
  • 2015年02月22日 13:09
  • 1068

【Struts框架】第一节Action-struts访问request等引用方法一

取得Map类型的request,session,application: LoginAction1.java: package cn.edu.hpu.getValue import java.ut...
  • u013517797
  • u013517797
  • 2015年03月13日 11:27
  • 816

【struts框架】第一节Action-路径问题与创建Action方法

1.路径问题 路径问题说明: struts中的路径问题是根据action的路径而不是jsp路径来确定,所以尽量不要使用相对路径。 虽然可以使用redirect方式解决,但redirect方式并非必...
  • u013517797
  • u013517797
  • 2015年02月22日 13:34
  • 1062

【Struts框架】第一节Action-action接受参数

Action接受参数的方式 方式1: 链接: javaee/user!add?name=jack&&age=21">添加用户 struts.xml:            /User_Add_s...
  • u013517797
  • u013517797
  • 2015年03月09日 19:25
  • 901
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:《Struts in Action》第12章翻译,初稿,第一节
举报原因:
原因补充:

(最多只允许输入30个字)