服务器端 JavaServer Faces 验证器

作者:Jennifer BallGreg Murray

问题描述

在一个事件(如表单)提交之后,HTTP 客户端(如浏览器)会向 Web 应用程序发送表单数据。HTTP 不允许将字符串以外的任何类型用于表示表单数据的键/值对。服务器端应用程序会将结果表单提交视为一组字符串对象。对于那些对数据有更多特定要求的 Web 应用程序来说,这可能是一个问题。验证是确认从客户端接收的表单数据是否符合数据格式,或是否处于应用程序要求的特定范围内的过程。

验证示例包括:

验证可以由客户端来执行,如提供客户端脚本验证功能(如使用 JavaScript[TM] 技术)的浏览器。无论是否执行客户端脚本,始终都应该执行服务器端验证。

通过验证,可以使 Web 应用程序更加安全。验证可以确保直接从客户端接收到的输入数据永远不会在数据库命令、传出 HTML 响应中使用,也不会用于运行 shell 命令。这些技术通常被恶意用户用来控制运行 Web 应用程序的系统。

JavaServer[TM] Faces 技术使用内置验证以及定制的解决方案,为开发者及页作者提供多种方法来定义服务器端验证。本文档提供使用 JavaServer Faces 技术的服务器端验证的策略。

解决方案

此解决方案将使用 JavaServer Faces 技术的内置工具来提供服务器端表单数据验证。本文档将讨论几种可以与 JavaServer Faces 技术一起使用的策略,下面列出了这些策略。

本文档对每个策略进行了详细的讨论。下表总结了各个策略的优点和缺点。

策略 优点 缺点
内置验证器 易于页作者进行配置。 提供的验证器数量有限。
定制验证器 易于添加到 UI 组件。验证代码在单一类中定义。 页作者不能配置验证参数。必须更改 Java[TM] 源代码才能更改验证。
带有可定制验证参数的定制验证器 易于页作者进行配置。使用扩展 javax.faces.webapp.ValidatorTag 的类提供定制验证器所使用的验证参数的精细配置。验证代码本质上成为可重复使用的组件。 开发者创建起来很复杂(需要了解定制操作和 JavaServer Faces 技术的基本详细资料)。需要使用 Java 代码来验证输入参数,因而减弱了可定制性。
使用托管 Bean 的验证 易于页作者进行配置。易于在托管 Bean 中定义验证代码。适于对托管 Bean 执行粗略的(应用程序范围)验证。 页作者无法配置每个 UI 组件的验证。需要使用多种方法来定制验证代码。

作为开发者,请基于自己的用例和上表中详细说明的缺点来选择最佳解决方案。

综上所述:

JavaServer Faces 技术提供了一个模型,JavaServer Faces 开发者或第三方可通过该模型提供大量的验证器,其数量可以超过 JavaServer Faces 实现所提供的验证器数量。这些验证器可以作为一个可重复使用的库。如果这种库适合您应用程序的需要,那么接下来就需要考虑如何利用该库。

本文档接下来的部分将会更加详细地描述每个策略。

内置验证器策略

JavaServer Faces 技术提供了一些内置验证器,用于执行基本操作,例如对数字(可以是浮点、长整型或整型数值)执行边界检查。这些验证器将被初始化并配置为托管 Bean。

您应该注意:使用内置验证器会产生一些副作用。当用户传递至少包含一个验证错误的表单时,将会重新呈现视图,并在其中显示错误消息。但是,该验证器会绕过 JavaServer Faces 生命周期的“更新模型值”和“调用应用程序”阶段,并会立即呈现出响应。当然,这样做有充分的理由;因为不应该使用被确定为无效的值来设置支持 Bean。但是,这也意味着:如果您的支持 Bean 在请求范围之内且发生了验证错误,则会跳过您页中的任何值绑定,并且相应地不会更新您的支持 Bean。例如,如果将隐藏文本字段绑定到支持 Bean 中的字符串属性 x,则出现验证错误时不会更新 x。如果其他 getter 方法依赖于要设置的 x,则呈现的响应可能会取决于不完整的数据设置。

JavaServer Faces 技术 1.1 版本提供下列验证器:

标记名 类名 描述
<f:validateDoubleRange> javax.faces.validator.DoubleRangeValidator 验证双精度的范围,适于处理货币。
<f:validateLongRange> javax.faces.validator.LongRangeValidator 验证长整型的范围。
<f:validateLength> javax.faces.validator.LengthValidator 验证表单变量的长度。

有两种使用验证器的方法。您可以将验证器定义并配置为托管 Bean,或使用相应的标记进行定义。

以下是有关页作者如何定义并配置其中一个内置验证标记的示例:

  <h:form id="validatorForm" onsubmit="return client_validation();">
<h:inputText id="bigNumber" value="#{ValidatorBean.bigNumber}">
<f:validateLongRange maximum="11" minimum="5"/>
</h:inputText>
<p>
<h:commandButton id="submit" action="success" value="Submit" />
</p>
<p>
<h:message style="color: orange" id="error3" for="bigNumber"/>
</p>
</h:form>

inputText 标记的子元素验证数字范围。在这种情况下,验证器的参数被设置为该标记的属性 (maximum/minimum)。无效数字将导致该页重新显示,并在 <h:message> 元素的位置呈现一条错误消息。

内置验证器可以提供精细的 UI 组件验证。内置验证器的属性也可以使用值绑定来设置。通过扩展示例,您也可将验证器指定为:

<f:validateLongRange minimum="#{MyBackingBean.minimum}" maximum="#{MyBackingBean.maximum}" />

在以上示例中,范围属性是在名为 MyBackingBean 的支持 Bean 中指定的。

UI 组件可以将 required 属性定义为 true,这样即使定义了 Null 或零长度字符串值,也会调用验证器,但是验证代码必须能够接受此类值。在 required 属性未设置为 true 的情况下,将不会调用该 UI 组件注册的验证器。

也可以定义内置验证器以提供应用程序范围的验证。在这种情况下,验证器将被定义并初始化为托管 Bean。然后,这些托管 Bean 就可以使用验证器属性上的方法绑定来验证 UI 组件的内容,验证方法与您使用托管 Bean 中定义的定制验证器方法相同。以下是使用其中一个内置验证器的应用程序作用域验证器托管 Bean 的示例。

  <managed-bean>
<description>
A Pre-defined JavaServer Faces Long Range Validator
</description>
<managed-bean-name>JSFValidator</managed-bean-name>
<managed-bean-class>javax.faces.validator.LongRangeValidator</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<managed-property>
<property-name>minimum</property-name>
<property-class>int</property-class>
<value>0</value>
</managed-property>
<managed-property>
<property-name>maximum</property-name>
<property-class>int</property-class>
<value>5</value>
</managed-property>
</managed-bean>

JSFValidator 的托管 Bean 定义使用内置的范围检查验证器,该验证器是通过将其中一个验证器指定为托管 Bean 而定义的。最大值和最小值范围在 JSFValidator Bean 上设置。

    <h:form id="validatorForm">
<p>Enter a number (between 0-5):</p>
<h:inputText id="number" value="#{ValidatorBean.number}"
validator="#{JSFValidator.validate}"/>
<p>
<h:message style="color: orange" id="errors2" for="number"/>
</p>
<p>
<h:commandButton id="submit" action="success" value="Submit" />
</p>
</h:form>

以上代码使用内置验证器(本示例中命名为 JSFValidator)验证所提供数字的值。如果该数字不在范围之内,则该页将重新显示,并包含一条错误消息。

托管 Bean 方法验证

可以按照托管 Bean 或支持 Bean 中定义的方法执行方法验证。方法绑定使用 UI 组件的验证器属性进行映射。

    <f:view>
<h:form id="validatorForm" >
<p>
Enter a new name (other than "blueprints").
</p>
<h:inputText id="userName" value="#{ValidatorBean.userName}"
validator="#{ValidatorBean.validate}"/>
<h:commandButton id="submit" action="success" value="Submit" />
<p>
<h:message style="color: red"
id="errors1"
for="userName"/>

</h:form>
</f:view>

以上 JavaServer Faces 页代码片段显示了一个输入文本字段,该字段使用名为 "ValidatorBean" 的托管 Bean 中称为 validate 的方法。验证器映射到的方法必须符合特定的方法签名。JavaServer Faces 运行环境为此方法提供必需的值。开发者只需提供验证输入的代码:

    public void validate(FacesContext context,
UIComponent component,
Object value) throws ValidatorException {

if ((context == null) || (component == null)) {
throw new NullPointerException();
}
if (value != null) {
if (((String)value).equals("blueprints")) {
throw new ValidatorException(new FacesMessage("blueprints is invalid"));
}
}
}

以上代码片段显示了绑定到 UIComponent 验证器属性的方法的方法签名。UIComponent 提供 FacesContext(对自身的引用)以及验证方法要使用的值。需要使用此方法来抛出 ValidatorException,以表示发生了验证错误。

以上图像显示在出现定制验证异常时会发生的情况。在这种情况下,将抛出一个 ValidatorException 验证错误,使得重新显示 JavaServer Faces 页,并在异常中显示错误消息。

一般情况下,一种很好的做法是将验证方法包含在相同的托管 Bean 中,该托管 Bean 定义了引用这些验证方法的组件的属性。因为这些方法可能需要访问组件的数据,以确定如何处理事件,或如何执行与组件相关联的验证。

一种很好的做法是使用 Java[TM] 2 SDK (java.util.ResourceBundle) 的 ResourceBundle 工具阻止在 Java 代码中出现硬编码错误消息。它还允许您对错误消息进行本地化。JavaServer Faces 技术提供了资源绑定工具,但是开发者仍需要确保消息格式的正确。

对于具备 Java 编码知识的开发者而言,将验证代码定义为托管 Bean 中的方法很容易。在这种情况下,验证器将被绑定到特定的 UI 组件或托管 Bean。虽然可通过托管 Bean 配置在一定程度上对托管 Bean 验证器进行定制,但这种定制是有限制的。

定制验证器

定制验证器方法与定制托管 Bean 方法非常类似。方法签名相同,但是对于定制验证器,方法名是 validate。开发者必须执行以下操作:

以下是来自 JavaServer Faces 配置文件的 XML 片段,声明了名为 DateValidator 的验证器。
  <validator>
<description>
Registers the Validator implementation,
DateValidator.
</description>
<validator-id>DateValidator</validator-id>
<validator-class>com.DateValidator</validator-class>
</validator>

JavaServer Faces 页会将该验证器与实现 javax.faces.component.EditableValueHolder 接口的 UI 组件进行关联。下面是使用 DateValidator 的 <h:inputText> 标记的示例。

    <h:inputText id="date1" value="11-12-99">
     <f:validator validatorId="DateValidator"/>
    </h:inputText>

实现 javax.faces.validator.Validator 接口的验证器被映射到一个使用直接子元素 <f:validator validatorId="DateValidator"/> 的 UI 组件。验证器类的验证方法包含验证逻辑,如下所示。

    public void validate(FacesContext context, UI component component, 
Object inputValue) {

boolean valid = false;
String value = (String)inputValue;

try {
DateFormat df = new SimpleDateFormat("mm-dd-yy");
Date d = df.parse(value);
if (d != null) valid = true;
} catch (java.text.ParseException px) {
valid = false;
} catch (java.lang.IllegalArgumentException iae) {
valid = false;
}

if (!valid) {
FacesMessage error = new FacesMessage("Date " + value +
" is invalid. Please enter a date conforming to " +
datePattern);
throw new ValidatorException(error);
}
}

请注意,此方法签名与托管 Bean 验证方法使用的方法签名相同。

使用验证器类方法的主要缺点是页作者无法基于每个 UI 组件配置验证参数。尽管 <c:set> 标记可用于在托管 Bean 上设置值,但这不是一项简单的任务,因为执行该操作需要详细了解托管 Bean 的机制以及 JavaServer Faces 生命周期模型的相关知识。另外,还可以将验证参数硬编码(使用上面示例中的日期
模式 "mm-dd-yy")为验证器类。在这样的情况下,这些值可以存储在某个语言环境特定的 java.util.ResourceBundle 类或属性文件中。

尽管此方法适合于提供 Web 应用程序的全局验证,但是在更改验证参数方面却不够灵活。

具有可定制验证参数的定制验证器

开发者可以向验证器实现类提供可定制验证参数,方法是:创建一个扩展 javax.faces.webapp.ValidatorTag 的定制操作。也可以使用 JavaServer Faces 页中的属性绑定表达式,为每个组件设置用于验证的参数。

开发者应该执行以下操作:

上图显示了创建具有可定制参数的 JavaServer Faces 验证器所需的工件。该验证器使用正则表达式指定日期模式。

在处理页时,JavaServer Faces 运行环境初始化 DateValidatorTagDateValidatorTag 创建一个 DateValidator 实例,并基于在页中指定的内容设置参数。DateValidator 验证方法如下所示。

    // this is called by the DateValidator tag when it is initialized
public void setDatePattern(String datePattern) {
this.datePattern = datePattern;
}

public void validate(FacesContext context, UIComponent component,
Object inputValue) {

boolean valid = false;
String value = (String)inputValue;
try {
valid = Pattern.matches(datePattern, value);
} catch(PatternSyntaxException pse) {
// something wrong with expression
}
if (!valid) {
FacesMessage error = new FacesMessage("Date " + value +
" is invalid. Please enter a date conforming to " +
datePattern);
throw new ValidatorException(error);
}
}

以上代码显示了验证器的验证方法,其中 datePattern 是使用定制操作设置的。这将允许页开发者基于每个 UI 组件来定义 datePattern。将抛出 ValidationException 来表示所提供值(在该示例中为 inputValue)中的错误。

可定制验证器需要开发者一方执行一些额外的工作。开发者应该开发扩展 javax.faces.webapp.Validator 的定制操作,并提供正确的标记库描述符文件。这样做的好处是可使用验证器进行 UI 组件的精细验证。尽管使用定制操作的验证器难以开发,但是它们易于页作者使用。

参考资料

有关本主题的详细信息,请参阅以下资料:


© Sun Microsystems 2005。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。