2019独角兽企业重金招聘Python工程师标准>>>

这个案例分析的目的是将一个单字段字符串值转换为一个  PhoneNumber对象。我们将一步一步地完成这个转换过程。

第 1 步:实现 Converter 接口

这一步实现 Converter接口。

 import javax.faces.convert.Converter; import org.apache.commons.lang.StringUtils; ... public class PhoneConverter implements Converter { ... }

第 2 步:实现 getAsObject 方法

这一步将一个字段值转换为一个 PhoneNumber对象。

public class PhoneConverter implements Converter { ... public Object getAsObject(FacesContext context, UIComponent component, String value) { if (StringUtils.isEmpty(value)){ return null;} PhoneNumber phone = new PhoneNumber(); String [] phoneComps = StringUtils.split(value," ,()-"); String countryCode = phoneComps[0]; phone.setCountryCode(countryCode); if ("1".equals(countryCode)){ String areaCode = phoneComps[1]; String prefix = phoneComps[2]; String number = phoneComps[3]; phone.setAreaCode(areaCode); phone.setPrefix(prefix); phone.setNumber(number); }else { phone.setNumber(value); } return phone; }     }

第 3 步:实现 getAsString 方法

这一步将一个 PhoneNumber对象转换为一个字符串。

public class PhoneConverter implements Converter { ... public String getAsString(FacesContext context, UIComponent component, Object value) { return value.toString(); }
}
public class PhoneNumber implements Serializable { ... public String toString(){ if (countryCode.equals("1")){ return countryCode + " " + areaCode + " " + prefix + " " + number; }else{ return number; } }
}

第 4 步:在 faces 上下文中注册自定义转换器

第 4 步可以以两种方式执行。第一种选择使用(比如)arcmind.PhoneConverter 的 id 来注册 PhoneConverter类。JSP 页中的  <f:converter/>标签会使用这个 id。下面是 第 4 步的选项 1 的代码:

 <converter> <converter-id>arcmind.PhoneConverter</converter-id> <converter-class>com.arcmind.converters.PhoneConverter</converter-class> </converter>

另一种方法是注册 PhoneConverter类来自动处理所有 PhoneNumber对象,如下所示。

 <converter> <converter-for-class>com.arcmind.value.PhoneNumber</converter-for-class> <converter-class>com.arcmind.converters.PhoneConverter</converter-class> </converter>

第 5 步:在 JSP 中使用转换器标签?

自然,下一步的执行取决于所选的注册方法。如果选择使用 arcmind.PhoneConverter 的 id 来注册 PhoneConverter类,那么就使用  <f:converter/>标签,如下所示。

 <h:inputText id="phone" value="#{UserRegistration.user.phone}"> <f:converter  converterId="arcmind.PhoneConverter" /> </h:inputText>

如果选择注册 PhoneConverter类来 自动 处理所有 PhoneNumber,那么就不需要在 JSP 页 中使用 <f:converter/>标签。下面是第 5  步的不带转换器标签的代码。

 <h:inputText id="phone" value="#{UserRegistration.user.phone}"> [Look mom no converter!] </h:inputText>

这样,我们已经完成了这个示例应用程序的转换处理代码!到目前为止完成的应用程序如下图所示。

JSF 验证

如前所述,JSF 验证可以确保应用程序数据包含预期的内容,例如:

  • java.util.Date 为 MM/yyyy 格式。

  • Float 在 1.0 和 100.0 之间。

在 JSF 中有 4 种验证:

  • 自带验证组件。

  • 应用程序级验证。

  • 自定义验证组件(它实现了 Validator接口)。

  • 在 backing bean 中的验证方法(内联)。

我们将在下面的讨论中介绍并展示每一种形式。

JSF 验证生命周期和组件

下图 显示了用户注册表单中名字字段的生命周期案例分析。代码引用被有意解释为 伪代码(pseudo-code)。

下面是 JSF 提供的一组标准验证组件:

  • DoubleRangeValidator:组件的本地值必须为数字类型,必须 在由最小和 / 或最大值所指定的范围内。

  • LongRangeValidator:组件的本地值必须为数字类型,并且可以转换 为长整型,必须在由最小和 / 或最大值所指定的范围内。

  • LengthValidator:类型必须为字符串,长度必须在由最小和 / 或 最大值所指定的范围内。

标准验证

在我们的示例应用程序中,用户的年龄可以是任意有效的整数(byte、short、int)。 因为将年龄设置为(比如说)-2是无意义的,所以可能要对这个字段添加一些验证。 下面是一些简单的验证代码,用以确保年龄字段中的数据模型完整性:

 <h:inputText id="age" value="#{UserRegistration.user.age}"> <f:validateLongRange maximum="150"minimum="0"/> </h:inputText>

完成年龄字段后,可能希望指定对名字字段的长度加以限制。可以像这样编写这个验证:

 <h:inputText id="firstName"value="#{UserRegistration.user.firstName}"> <f:validateLength minimum="2" maximum="25" /> </h:inputText>

下图显示了由上面标准验证示例所生成的默认详细验证消息。

尽管 JSF 自带的验证在许多情况下都可以满足,但是它有一些局限性。 在处理电子邮件验证、电话号码、URL、日期等数据时,有时编写自己的验证 器会更好一些,不过我们将在稍后对此进行讨论。

应用程序级验证

在概念上,应用程序级验证实际上是业务逻辑验证。JSF 将表单和 / 或字段级 验证与业务逻辑验证分离开。应用程序级验证主要需要在 backing bean 中添加代码,用这个模型确定绑定到模型中的数据是否合格。对于购物车,表单级验证 可以验证输入的数量是否有效,但是需要使用业务逻辑验证检查用户是否超出了他或者 她的信用额度。这是在 JSF 中分离关注点的另一个例子。

例如,假定用户单击了绑定到某个操作方法的按钮,那么就会在调用应用程序阶段调用 这个方法(有关的细节,请参见第一幅图)。假定在更新模型阶段进 行了更新,那么在对模型数据执行任何操纵之前,可以添加一些验证代码,根据应用程序的业务规则检查输入的数据是否有效。

例如,在这个示例应用程序中,用户单击了 Register按钮,这个按钮被绑定到应用程序控制器的 register()方法。 我们可以在 register()方法中添加验证代码,以确定名字字段是否为 null。如果该字段为 null,那么还可以在  FacesContext中添加一条消息,指示相关组件返回到当前页。

其实它现在并不是业务规则逻辑的一个好例子。更好的例子是检查用户是否 超出了她或者她的信用额度。在该例中,不是检查字段是否为空,我们可以调用模型对象的方法来确保当前用户已经不在系统中。

下图描绘了这个过程:

注意在 register()方法中,消息是如何以   ${formId}:${fieldId}的形式添加到 FacesContext中的。 图 12 显示了消息与组件 id 之间的关系。

验证消息:

Message id added as ${formId} ;{FIELDId}
<h:message associated with fieldId
(use <h:messages to display all messages)

应用程序级验证的优缺点

应用级验证非常直观并且容易实现。不过,这种形式的验证是在其他形式的验证 (标准、自定义、组件)之后发生的。

应用程序级验证的优点如下:

  • 容易实现。

  • 不需要单独的类(自定义验证器)。

  • 不需要页编写者指定验证器。

应用程序级验证的缺点如下:

  • 在其他形式的验证(标准、自定义)之后发生。

  • 验证逻辑局限于 backing bean 方法,使得重用性很有限。

  • 在大型应用程序和 / 或团队环境中可能难于管理。

最终,应用程序级验证只应该用于那些需要业务逻辑验证的环境中。

自定义验证组件

对于标准 JSF 验证器不支持的数据类型,则需要建立自己的自定义验证组件,其中包括电子邮件地址和邮政编码。如果需要明确控制显示给最终用户的消息, 那么还需要建立自己的验证器。在 JSF 中,可以创建可在整个 Web 应用程序中重复使用 的可插入验证组件。

创建自定义验证器的步骤如下,我们将一步步地分析:

  1. 创建一个实现了 Validator接口的类 (javax.faces.validator.Validator)。

  2. 实现 validate方法。

  3. 在 faces-confix.xml 文件中注册自定义验证。

  4. 在 JSP 页中使用 <f:validator/>标签。

下面是创建自定义验证器的分步示例代码。

第 1:实现 Validator 接口

第一步是实现 Validator接口。

 import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException; ... public class ZipCodeValidator implements Validator{ private boolean plus4Required; private boolean plus4Optional; /** Accepts zip codes like 85710 */ private static final String ZIP_REGEX = "[0-9]{5}"; /** Accepts zip code plus 4 extensions like "-1119" or " 1119" */ private static final String PLUS4_REQUIRED_REGEX = "[ |-]{1}[0-9]{4}"; /** Optionally accepts a plus 4 */ private static final String PLUS4_OPTIONAL_REGEX = "([ |-]{1}[0-9]{4})?"; ... }

第 2 步:实现验证方法

接下来,需要实现 validate方法。

public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException { /* Create the correct mask */ Pattern mask =  null; /* more on this method later */ initProps(component); if (plus4Required){ mask = Pattern.compile(ZIP_REGEX + PLUS4_REQUIRED_REGEX); } else if (plus4Optional){ mask = Pattern.compile(ZIP_REGEX + PLUS4_OPTIONAL_REGEX); } else if (plus4Required && plus4Optional){ throw new IllegalStateException("Plus 4 is either optional or required");} else { mask = Pattern.compile(ZIP_REGEX); } /* Get the string value of the current field */ String zipField = (String)value; /* Check to see if the value is a zip code */ Matcher matcher = mask.matcher(zipField); if (!matcher.matches()){ FacesMessage message = new FacesMessage(); message.setDetail("Zip code not valid"); message.setSummary("Zip code not valid"); message.setSeverity(FacesMessage.SEVERITY_ERROR); throw new ValidatorException(message); } }

第 3 步:在 FacesContext 中注册自定义验证器

您现在应该熟悉在 FacesContext中注册自定义验证器的代码了。

<validator> <validator-id>arcmind.zipCodeValidator</validator-id> <validator-class>com.arcmind.jsfquickstart.validation.ZipCodeValidator</validator-class>
</validator>

第 4 步:在 JSP 中使用 <f:validator/> 标签

<f:validator/>标签声明使用  zipCodeValidator<f:attribute/> 标签将 plus4Optional属性设置为 true。 注意,它定义了 inputText 组件的属性,而 不是验证器的属性!

  <h:inputText id="zipCode" value="#{UserRegistration.user.zipCode}"> <f:validator validatorId="armind.zipCodeValidator"/> <f:attribute name="plus4Optional" value="true"/> </h:inputText>

为了读取 zipCodeinputText组件的 plus4Optional属性,请完成以下步骤::

 private void initProps(UIComponent component) { Boolean optional = Boolean.valueOf((String) component.getAttributes(). get("plus4Optional")); Boolean required = Boolean.valueOf((String) component.getAttributes(). get("plus4Required")); plus4Optional = optional==null ? plus4Optional : optional.booleanValue(); plus4Required = required==null ? plus4Optional : required.booleanValue(); }

总体而言,创建自定义验证器是相当直观的,并且可以使该验证在许多应用程序中重复使用。缺点是必须创建一个类,并在 faces 上下文中管理验证器注册。 不过,通过创建一个使用这个验证器的自定义标签,使其看上去像是一个自带的验证,可以进一步实现自定义验证器。对于常见的验证问题,如电子邮件 验证,这种方法可以支持这样一种设计理念,即代码重用和一致的应用程序行为是 最重要的。

backing bean 中的验证方法

作为创建单独的验证器类的替代方法,可以只在 backing bean 的方法中实现自定义 验证,只要这个方法符合 Validator接口的  validate方法的参数签名即可。例如,可以编写以下方法:

 [SomeBackingBean.java] public void validateEmail(FacesContext context, UIComponent toValidate, Object value) { String email = (String) value; if (email.indexOf('@') == -1) { ((UIInput)toValidate).setValid(false); FacesMessage message = new FacesMessage("Invalid Email"); context.addMessage(toValidate.getClientId(context), message); } }

之后,可通过如下所示的 validator属性在 JSF 中使用这个方法:

  <h:inputText id="email" value="#{UserRegistration.user.email}"validator="#{UserRegistration.validateEmail}"   required="true"> </h:inputText>

JSF 用 validateEmail方法对绑定到   user.email模型属性的 inputText 组件值进行自定义验证。如果电子邮件格式无效,那么就在相关组件的 faces 上下文中添加 消息。考虑到这种验证方法实际上是 backing bean 的一部分,为什么通常必须用某个值与相关组件的关联来评估该值,而不是直接检查本地 bean 属性呢?线索就在前面的生命周期图中。如果现在不能马上找到 答案,也不要担心,我们将在本文的最后对此加以说明。

默认验证

注意上面 email标签的 required属性。 利用 required 属性是一种 默认验证形式。如果这个属性是  true,那么相应的组件必须有一个值。一个重要的 说明:如果 required属性为 false, 那么就不用对这个标签 / 组件指派验证,这样,JSF 将跳过对这个组件的验证,并让值和组件的状态保持不变。

下图显示出我们讨论过的验证形式:

自定义消息

您可能注意到了,JSF 提供的默认转换和验证消息非常长,这会让那些总是输入无效表单数据的最终用户感到困惑和恼火。幸运的是,您可以通过 创建自己的消息资源绑定来改变 JSF 提供的默认消息。jsf-impl.jar  (或类似的文件中)中包含了一个 message.properties 文件,该文件包含图 14   所示的默认消息。

下图是默认的JSF转换和验证消息

通过创建自己的 message.properties 文件并断开指定场所的 faces 上下文中绑定 的消息资源,您可以更改默认消息,如下图所示。

关于在 JSF 中创建自定义转换和验证消息的更多内容请参前阅参考资料。

处理 JSF 生命周期

我们在本文前面留下了一些问题让您考虑,现在可以解决它们了! 我们提到的一件事是对 UICommand按钮使用 immediate 属性,比如 commandLink或者 commandButtons。现在请您考虑希望在什么样的场景中跳过验证。

基本上只要用户需要输入数据,就需要对这个数据进行验证。不过,如果整个数据 项是可选的,那么就不需要进行验证。一种避免 JSF 生命周期的验证阶段的方法是利用  UICommand组件的 immediate属性,该属性可以在处理验证阶段 之前的应用请求值阶段期间(而不是在处理验证阶段 之后的调用应用程序阶段) 强制调用这个操作。

immediate属性允许您通过标准浏览规则控制 页流程,并绕过验证。可以针对特定的场景实现这项技术,比如带有可选步骤和 / 或表单的在线 向导(如当用户单击 Skip按钮以进入下一视图),或者在用户因为某种原因而取消某个表单的情况下。

我们在本文中留下的第二个问题是:既然验证方法实际上是 backing bean 的 一部分,那么为什么通常必须利用组件关联来判断它的值。请参阅前面的  JSF 应用程序生命周期,看看您能否找到答案。

这里的密诀是:尽管 validateEmail嵌入 验证方法是实际的 backing bean 的一部分,但是该方法必须通过组件关联来引用这,而不是直接访问本地属性来引用值。由于验证发生在组件值绑定到模型 之前(在更新模型值阶段),所以模型处于未知状态。 因此,必须编写嵌入自定义验证逻辑,就像使用一个自定义 Validator对象处理验证一样。这也解释了维护相同方法签名的需求。

这些尚待解决的枝节问题有什么意义呢,当然,它们最终将我们带回 JSF 应用程序生命周期。将这些问题汇总在一起,就能体现充分理解生命周期的重要性 —— 向后、向前或由内向外,这样您就可以在需要的时候操纵它。

结束语

在本文中我们讨论了相当多的 JSF 转换和验证的基本内容。事实上, 我们讨论了在自己的应用程序中使用这些过程需要知道的大部分内容 (至少对这个版本的 JSF 而言)!

当然,我们不可能讨论到 所有内容。例如,您可能想要了解 MyFaces (请参阅 参考资料)中 JSF 没有提供、或者这里没有讨论到的验证器组件。 此外,虽然我们讨论了大多数常用的转换和验证技术,但还有一些没有包含在内。 例如,在编写自定义组件时,可以在组件的解码 / 编码过程中直接处理转换和 / 或验证 (取决于组件的类型及其功能),但是我们只能将对自定义组件开发的更深入讨论留 到以后进行了。

其他要牢记的是转换和验证不一定会很好地协同工作。转换将字符串转换 为对象,而大多数标准验证是对字符串进行的。因此,在同时使用自定义转换 和验证必须格外小心。例如,PhoneNumber 对象不能与长度验证器一起使用。在这种情况下,要么编写自定义验证器,要么在自定义转换器中添加一个特别的验证逻辑。我们偏向后一种方法,因为 它让我们可以将自定义转换器(自带验证逻辑)与特定的对象类型相关联,并让  JSF 处理这种对象类型。JSF 自动为我们做这项工作,不需要在 JSP 中包含任何 特定的转换器 id。(当然,有人会称它为懒惰编程,它也不是对所有用例都适用的最佳解决方案。)

我们认为本月文章中的讨论再次声明了以下这点,即 JSF 提供了一种灵活的、强大的可插入式 Web 应用程序开发框架。除了标准转换器和验证器之外,JSF 还可以促进同时满足应用程序和框架开发人员的要求的自定义实现。最终,要由您来确定选择何种转换和验证策略。JSF 使您能够在原型制造阶段很快、很容易地上手(标准转换器、验证器、内部验证等),并在以后的开发阶段移植到更 复杂的生产解决方案中(自定义对象、自定义消息等)。JSF 生命周期在所有阶段都提供了 可靠的基础设施,始终如一地保证数据模型的完整性。

转载于:https://my.oschina.net/guopengfei/blog/374768

Java Server Faces (JSF)页面转换与验证(二)相关推荐

  1. Java Server Faces(JSF)历险(一)

    从今天开始研究JSF(Java Server Faces),JSF作为最新的MVC框架,得到了sun等业界技术领袖的支持,我相信JSF必然会超越Struts,Tapestry成为主流开发技术.以后的系 ...

  2. java server faces

    引用:http://baike.baidu.com/view/3420290.htm 材料参考:http://wenku.baidu.com/view/83b1f8659b6648d7c1c74667 ...

  3. java的jsf是什么_什么是 JSF(Java Server Faces)

    JSF为JAVA的 Web应用用户界面的开发人员提供了标准的编程接口.丰富可扩展的UI组件库(一个核心的JSP标记库用来处理事件.执行验证以及其他非UI相关的操作和一个标准的HTML 标记库来表示 U ...

  4. Java Server Faces_用JavaServer Faces开发Web应用(4) (转)

    用JavaServer Faces开发Web应用(4) (转)[@more@] 6.事件处理. XML:namespace prefix = o ns = "urn:schemas-micr ...

  5. 【Java代码】坐标系说明+WGS84\GCJ02\BD09坐标系转换工具+Java坐标系转换及验证源代码分享(粘贴可用)

    1. 坐标系说明 开发地图应用服务时,一定会接触到各种坐标系,而保证坐标系的正确与合理是一切数据分析的前提,总的来说,坐标系可以分为两大类:地理坐标系GCS(Geographic Coordinate ...

  6. java验证网址正常打开_JSP 页面访问用户验证

    jsp安全性问题,当别人知道某个jsp文件的网址后就可以跳过登陆页面直接访问该jsp文件了,这样无法禁止外部无权限用户的访问.本文讨论内容是通过权限验证的用户,才可以访问特定的页面. JSP 页面验证 ...

  7. java linux urlencode_java字符编码转换研究(转)

    1. 概述 本文主要包括以下几个方面:编码基本知识,java,系统软件,url,工具软件等. 在下面的描述中,将以"中文"两个字为例,经查表可以知道其GB2312编码是" ...

  8. 将动态aspx页面转换成为静态html页面的几种方法

    1.         模版法 该方法历史悠久,具体处理流程为采用一个html模版,将其中的关键字替换为我们希望的信息. 优点: 缺点: 所有的信息都要采取字符串批凑的方式来实现,比如需要一个列表,就需 ...

  9. HTML页面转换asp,将asp页面转换成html页面 代码

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 将asp页面转换成html页面 Sub GenFile(id) dim objXmlHttp set objXmlHttp = Server.Create ...

最新文章

  1. 关于python创建类的深入理解
  2. redis入门(数据类型)
  3. XyPlayer 智能解析 X4 影视解析源码
  4. 新功能:阿里云负载均衡SLB支持HTTP/HTTPS超时时间自定义功能
  5. 2021 年了,算法岗位应该怎样准备面试?
  6. 如何在 Web 开发中找到第一份工作?
  7. 深度学习进阶NLP:word2vec的高速化
  8. 【智驾深谈】想拿自动驾驶融资,先过VC这16问
  9. 计算机组成原理————寄存器寻址方式大总结
  10. 租服务器的 直连100m是啥,如何知道我的服务器带宽是独享10M或者100M?
  11. DIY 简单又好吃的香果魔芋
  12. 图 邻接矩阵幂的含义 离散数学定理14.11
  13. websocket 单机服务 和 分布式集群解决方案
  14. Linux查询网络配置相关命令
  15. 北京理工大学计算机学院赵曜,中国进出口银行2016年度拟接收毕业生情况公示...
  16. 统一信息管理平台服务器,4A统一安全管控平台
  17. 静默安装VC_redist.x64.exe
  18. store update、insert或delete语句影响了意外的行数(0)。自加载实体后,实体可能已被修改或删除
  19. BaseBlockCipher(bouncycastle)
  20. JavaEE_文件上传、文件下载

热门文章

  1. MyBatis框架(6)动态sql
  2. pythoon介绍、安装环境、基础知识、练习题
  3. 微信浏览器中页面刷新
  4. CentOS 中卸载 RPM 包文件
  5. 闭包函数 use 改变外部变量
  6. iOS开发中的错误整理,Changing the delegate of a tab bar managed by a tab bar controller is not allowed...
  7. Android Studio IDE Out of Memory
  8. struts 国际化
  9. ASP.NET MVC资源文件多语言实现方式
  10. 如何实现实时文本过滤