3.3 使用转换器完成类型转换
虽然JSF框架希望模拟传统C/S架构应用的开发模式,但它本质上依然是基于请求-响应架构的Web框架,因此所有请求参数必然是字符串类型。但JSF将这些请求参数绑定到托管Bean的属性时,托管Bean属性的类型是多种多样的,因此必然涉及类型转换。
3.3.1 转换器概述、用途
所有的MVC框架,都需要负责解析HTTP请求参数,并将请求参数传给控制器组件。此时问题出现了:HTTP请求参数都是字符串类型,但Java是强类型的语言,因此MVC框架必须将这些字符串参数转换成相应的数据类型——这个工作是所有的MVC框架都应该提供的功能。
表现层数据的流向以及所需的类型转换如图3.16所示。
图3.16 表现层数据的流向和类型转换
当某个组件绑定到一个托管Bean的属性时,该组件的值通常有两个数据类型:
字符串类型:当该组件在页面上呈现时,该组件的值只能是字符串类型,用户可以读取或修改该组件的值。
真实类型:当把该组件的值绑定到托管Bean的属性时,该组件的值就会转换为与托管Bean的属性相同的类型。
JSF提供了非常强大的类型转换机制,当开发者将页面上UI组件的值绑定到托管Bean的属性时,JSF框架将会在后台完成类型转换。正如前面示例程序中看到的,当托管Bean中的price属性是Double类型时,JSF将会自动把绑定到price属性的UI组件的值转换为Double类型。上面这个转换过程对于普通开发者来说完全透明,实际上这得益于JSF内置的各种转换器。
除此之外,JSF还提供了很好的扩展性,开发者可以非常简单地开发出自己的类型转换器,完成字符串和自定义复合类型之间的转换(例如,完成字符串到Person实例的转换)。
正如大家都知道的,Java的类型转换完全可能出现错误,如果类型转换出现错误,JSF的消息机制就会起作用,JSF的消息机制会自动处理该异常,并且在页面上生成提示信息。
表现层的另一个数据处理是输入校验,输入校验可分为客户端校验和服务器端校验两种。客户端校验和服务器端校验都是必不可少的,二者分别完成不同的过滤。
客户端校验进行基本校验,如检验非空字段是否为空,数字格式是否正确等。客户端校验主要用来过滤用户的误操作。客户端校验的作用是:拒绝误操作输入提交到服务器处理,降低服务器端负担。
服务器端校验也必不可少,服务器端校验防止非法数据进入程序,导致程序异常、底层数据库异常。服务器端校验是保证程序有效运行及数据完整的手段。
下一节将会详细介绍输入校验的知识。
3.3.2 JSF内建转换器
通常来说,当把组件的值或组件本身绑定到托管Bean的属性时,JSF会自动完成两种类型之间的转换,无须开发者关心。例如,我们把UISelectBoolean组件绑定到boolean类型的Bean属性,JSF就可以自动完成所需的类型转换。除此之外,JSF中的一些特殊组件必须绑定到特定类型的属性,就拿UISelectBoolean组件来说,它只能被绑定到java.lang.Boolean类型或boolean类型的属性。
在默认情况下,JSF依靠内建转换器来完成自动类型转换,JSF内建支持的转换器位于javax.faces.convert包下,该包提供了如下类型转换器:
BigDecimalConverter
BigIntegerConverter
BooleanConverter
ByteConverter
CharacterConverter
DateTimeConverter
DoubleConverter
EnumConverter
FloatConverter
IntegerConverter
LongConverter
NumberConverter
ShortConverter
上面每个内置转换器都关联一个标准的错误消息,如果用户输入的数据进行类型转换失败时,该错误消息可提示用户重新输入。关于内置转换器的错误消息后面会有更详细的介绍。
对于上面绝大部分转换器,开发者无须显式地调用它们。但由于DateTimeConverter、NumberConverter两个转换器可能需要重复显式使用,因此JSF为它们提供了两个专门的标签:
<f:convertDateTime…/>:为DateTimeConverter转换器提供的标签。
<f:convertNumber…/>:为NumberConverter转换器提供的标签。
除此之外,JSF还提供了一个<f:converter…/>标签,该标签通常用于使用自定义转换器,后面会有更详细的解释。
3.3.3 使用转换器
JSF使用内建转换器有两种情形:
自动使用:当开发者将UI组件的值绑定到Bean属性时,系统会自动进行类型转换。
通过专用标签引用:通过<f:convertDateTime…/>、<f:convertNumber…/>两个标签进行显式转换。
对于自动使用JSF内建转换器的情形,本书前面已有不少示例,此处不再赘述。下面介绍通过<f:convertDateTime…/>、<f:convertNumber…/>两个标签使用转换器的情形。
使用<f:convertDateTime…/>标签可以完成日期时间和字符串之间的转换。使用该标签时可以指定如下属性:
dateStyle:指定日期风格,该属性值与java.text.DateFormat中定义的日期风格对应。该属性的属性值支持short、medium、long和full等值。如果没有指定该属性值,则默认使用default。需要指出的是,只有当该标签的type属性为date且未指定pattern属性时,该属性才会起作用。
locale:指定格式化该日期、时间所使用的Locale,如果没有指定该属性,则使用由FacesContext.getLocale()方法返回的Locale作为默认值。
pattern:指定对日期格式进行格式化的模式字符串。该参数的作用和SimpleDateFormat提供的format方法中pattern参数的作用一样。
timeStyle:指定时间风格,该属性值与java.text.DateFormat中定义的时间风格对应。该属性的属性值支持short、medium、long和full等值。如果没有指定该属性值,则默认使用default。需要指出的是,只有当该标签的type属性为time且未指定pattern属性时,该属性才会起作用。
timeZone:该属性指定该日期、时间对应的市区信息。
type:指定该字符串是包含日期、时间还是同时包含。该属性的有效值为"date"、"time"和"both,默认值是"date"。
binding:该属性用于将转换器本身绑定到托管Bean的属性。
提示
JSF的DateTimeConverter转换器主要依赖于Java的DateFormat、SimpleDateFormat两个工具类,因此它的很多属性都与它们方法的参数相对应,读者可以参考疯狂Java体系的《疯狂Java讲义》来了解关于这些参数的更多知识。
例如,有如下示例:
<h:outputText value="user. birthday "> <f:convertDateTime dateStyle="full"/> </h:outputText>
上面代码的作用就相当于在页面上输出如下Java代码:
DateFormat.getDateInstance(DateFormat.FULL).format(user.birthday)
再看下面示例:
<h:outputText value="user.birthday"> <f:convertDateTime timeStyle="short" type="time"/> </h:outputText>
上面代码的作用就相当于在页面上输出如下Java代码:
DateFormat.getTimeInstance(TimeFormat.SHORT).format(user.birthday)
大部分时候,我们可以为<f:convertDatetime…/>标签指定pattern属性,通过该属性既可完成日期、时间到字符串的转换,也可轻松完成字符串到日期、时间的转换。
下面示例既使用<f:convertDateTime…/>将输入组件中的字符串输入转换为日期,也使用<f:convertDateTime…/>对输出组件的日期、时间进行格式化。首先看如下页面代码。
程序清单:codes\03\3.3\DatetimeConverter\add.jsp
<h:form>
用户名:<h:inputText value="#{userBean.name}"/><br/>
生日:<h:inputText value="#{userBean.birthday}">
<!-- 指定用户按指定格式输入日期、时间 -->
<f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss"/>
</h:inputText>(请按yyyy-MM-dd HH:mm:ss格式输入)<br/>
<h:commandButton value="添加" action="#{userBean.add}"/>
</h:form>
上面页面代码中粗体字代码的转换器标签指定了pattern参数,这意味着只要用户输入的字符串能匹配该模式字符串,该转换器就可将用户输入转换成日期、时间。当然,由于此处我们显式使用DateTimeConverter对userBean.birthday进行转换,这就要求userBean托管Bean的birthday属性必须是java.util.Date类型。
处理完成后,另一个页面上输出userBean托管Bean的birthday属性,如果输出时只要保留日期部分,无须时间部分,我们也可使用如下转换器对该属性进行格式化输出。
程序清单:codes\03\3.3\DatetimeConverter\show.jsp
<h1>添加成功</h1>
<h:outputText value="#{userBean.name}"/><br/>
<h:outputText value="#{userBean.birthday}">
<!-- 转换出来的日期、时间将只包括日期部分 -->
<f:convertDateTime dateStyle="full"
type="date"/>
</h:outputText>
上面粗体字代码指定type="date",这意味格式化输出该日期、时间时将只保留日期部分,且采用DateFormat.FULL的日期风格进行格式化。
如果在第一个页面的生日框中按yyyy-MM-dd HH:mm:ss格式输入2000-12-12 20:12:22字符串,JSF应用转换成功后将看到如图3.17所示的界面。
图3.17 使用DateTimeConverter转换器
从上面示例可以看出,使用DateTimeConverter可以完成两方面工作:
字符串到日期的转换。
日期到字符串的转换,该转换实质上就是一种格式化输出。
与DateTimeConverter转换器类似的是,NumberConverter转换器也有类似的功能,只是该转换器是完成数值和字符串之间的转换。JSF为该转换器提供了<f:convertNumber…/>标签,该标签支持如下属性:
currencyCode:该属性值必须为ISO 4217货币代码之一,仅当格式化货币时该属性才有效。
currencySymbol:该属性指定货币符号,仅当格式化货币时该属性才有效。
groupingUsed:指定格式化数值时是否使用分组符。该属性值只能是true或false,默认是true。
integerOnly:该属性指定是否只解析处理数值的整数部分。
locale:指定解析或格式化数值所使用的Locale。
maxFractionDigits:指定小数部分最多保留多少位。
maxIntegerDigits:指定整数部分最多保留多少位。
minFractionDigits:指定小数部分最少保留多少位。
minIntegerDigits:指定整数部分最少保留多少位。
pattern:该属性用于指定一个模式字符串,指定转换器将根据自定义模式字符串进行格式化。
type:指定解析和格式化该数值的风格。该属性支持number、currency或percentage三个值之一,它们与NumberFormat提供的getNumberInstance、getCurrencyInstance、getPercentInstance三个方法相对应。
binding:该属性用于将转换器本身绑定到托管Bean的属性。
提示
JSF的NumberConverter转换器主要依赖于Java的NumberFormat及其子类DecimalFormat,因此它的很多属性都与它们方法的参数相对应,读者可以参考疯狂Java体系的《疯狂Java讲义》中关于NumberFormat的知识。
例如,如下代码使用<f:convertNumber…/>标签来执行转换。
<h:outputText value="bookBean.price">
<f:convertNumber type="currency"/>
</outputText>
下面示例示范使用了pattern模式字符串对数值进行转换的情形。我们首先提供如下托管Bean类。
程序清单:codes\03\3.3\NumberConverter\WEB-INF\src\org\crazyit\jsf\BookBean.java
public class BookBean { private String name; private Double price; //无参数的构造器 public BookBean() { } //初始化全部属性的构造器 public BookBean(String name , Double price) { this.name = name; this.price = price; } //省略name属性的setter和getter方法 … //省略price属性的setter和getter方法 … }
上面托管Bean中的price属性是Double类型,我们在faces-config.xml文件中配置该托管Bean,并为它的name、price属性指定初始值。下面是该托管Bean的配置代码。
程序清单:codes\03\3.3\NumberConverter\WEB-INF\faces-config.xml
<!-- 配置托管Bean --> <managed-bean> <!-- 设置托管Bean的名字 --> <managed-bean-name>bookBean</managed-bean-name> <!-- 设置托管Bean的实现类 --> <managed-bean-class>org.crazyit.jsf.BookBean</managed-bean-class> <!-- 设置托管Bean实例的有效范围 --> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>name</property-name> <value>疯狂Java讲义</value> </managed-property> <managed-property> <property-name>price</property-name> <value>99</value> </managed-property> </managed-bean>
上面配置文件中粗体字代码指定了price属性的初始值是99,如果我们希望对price属性进行格式化输出,格式化后整数部分占4位、小数部分占2位,则可以使用如下JSP页面代码。
程序清单:codes\03\3.3\NumberConverter\show.jsp
<h1>查看图书</h1> <h:outputText value="#{bookBean.name}"/><br/> <h:outputText value="#{bookBean.price}"> <!-- 转换出来的日期、时间将只包括日期部分 --> <f:convertNumber pattern="¥0000.00"/> </h:outputText>
上面页面中为<f:convertNumber…/>指定了pattern="¥0000.00",这就可以将price属性格式化成整数部分占4位、小数部分占2位的字符串。在浏览器中浏览该页面会看到如图3.18所示的效果。
图3.18 使用NumberConverter执行格式化输出
3.3.4 转换失败后的错误消息
当JSF转换器执行类型转换时不可避免地会遇到转换失败的情形,当类型转换失败时JSF会将转换失败后的错误显示在页面上。
为了在页面上显示错误消息,可以借助于JSF提供的如下两个标签:
<h:message…/>:显示指定UI组件类型转换失败、输入校验失败后的错误消息。
<h:messages…/>:显示某个页面中所有组件转换失败、输入校验失败后的错误消息。
使用<h:message…/>标签时可指定如下属性:
for:该属性是必填属性,该属性指定某个UI组件的id属性值,该属性指定该标签用于输出哪个UI组件转换失败、校验失败后的提示信息。
rendered:该属性指定该标签显示的错误消息是否输出。
binding:该属性用于将该标签本身绑定到托管Bean的属性。
使用<h:messages…/>标签时可指定如下属性:
globalOnly:指定是否仅仅显示全局消息。
rendered:该属性指定该标签显示的错误消息是否输出。
layout:指定采用何种方式来对多个组件类型转换失败、输入校验失败后的错误消息进行布局,该属性支持table、list两个属性值。如果指定为table,则意味着使用HTML表格来进行布局;如果指定为list,则意味着使用HTML列表方式来进行布局。
binding:该属性用于将该标签本身绑定到托管Bean的属性。
例如,前面示例中添加用户时如果输入的用户生日不满足要求的格式,那么JSF进行类型转换时将会失败,只要使用<h:message…/>标签输出该UI组件类型转换失败的提示信息即可。
修改后的add.jsp页面代码如下:
程序清单:codes\03\3.3\ErrorMessage\add.jsp
<h1>添加用户</h1> <h:form> 用户名:<h:inputText value="#{userBean.name}"/><br/> 生日:<h:inputText value="#{userBean.birthday}" id="birthday"> <!-- 指定用户按指定格式输入日期、时间 --> <f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss"/> </h:inputText>(请按yyyy-MM-dd HH:mm:ss格式输入) <!-- 指定输出birthday组件的值类型转换失败、校验失败的错误信息 --> <h:message for="birthday"/><br/> <h:commandButton value="添加" action="#{userBean.add}"/> </h:form>
上面页面代码中粗体字代码就用于输出birthday的类型转换失败、校验失败的错误提示信息,如果输入的字符串不能成功转换,将看到如图3.19所示的页面。
图3.19 转换失败后的错误提示信息
从图3.19可以看出,<h:message.../>输出了birthday组件的值转换失败的提示信息,不过该提示信息全部是英文,这对简体中文应用来说并不适合。
JSF应用UI组件的值类型转换失败、输入校验失败后的错误消息可来自3个方面:
来自JSF为内置转换器、校验器提供的默认消息。
来自开发者通过自定义消息资源文件提供的错误消息。
来自各UI组件标签的converterMessage属性指定的错误消息。
正如前面指出的,JSF所有内置转换器都有默认的错误提示消息,当转换失败时,<h:message…/>将会输出这些默认的错误提示消息。关于JSF内置转换器的标准错误消息可以参考JSF规范的2.5.2.4节。下面列出JSF所有内置转换器、校验器的标准错误消息。
javax.faces.component.UIInput.CONVERSION={0}: Conversion error occurred
javax.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required
javax.faces.component.UIInput.UPDATE={0}: An error occurred when processing your submitted information
javax.faces.component.UISelectOne.INVALID={0}: Validation Error: Value is not valid
javax.faces.component.UISelectMany.INVALID={0}: Validation Error: Value is not valid
javax.faces.converter.BigDecimalConverter.DECIMAL={2}: ''{0}'' must be a signed decimal number.
javax.faces.converter.BigDecimalConverter.DECIMAL_detail={2}: ''{0}'' must be a signed decimal number consisting of zero or more digits, that may be followed by a decimal point and fraction. Example: {1}
javax.faces.converter.BigIntegerConverter.BIGINTEGER={2}: ''{0}'' must be a number consisting of one or more digits.
javax.faces.converter.BigIntegerConverter.BIGINTEGER_detail={2}: ''{0}'' must be a number consisting of one or more digits. Example: {1}
javax.faces.converter.BooleanConverter.BOOLEAN={1}: ''{0}'' must be 'true' or 'false'.
javax.faces.converter.BooleanConverter.BOOLEAN_detail={1}: ''{0}'' must be 'true' or 'false'. Any value other than 'true' will evaluate to 'false'.
javax.faces.converter.ByteConverter.BYTE={2}: ''{0}'' must be a number between 0 and 255.
javax.faces.converter.ByteConverter.BYTE_detail={2}: ''{0}'' must be a number between 0 and 255. Example: {1}
javax.faces.converter.CharacterConverter.CHARACTER={1}: ''{0}'' must be a valid character.
javax.faces.converter.CharacterConverter.CHARACTER_detail={1}: ''{0}'' must be a valid ASCII character.
javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a date.
javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' could not be understood as a date. Example: {1}
javax.faces.converter.DateTimeConverter.TIME={2}: ''{0}'' could not be understood as a time.
javax.faces.converter.DateTimeConverter.TIME_detail={2}: ''{0}'' could not be understood as a time. Example: {1}
javax.faces.converter.DateTimeConverter.DATETIME={2}: ''{0}'' could not be understood as a date and time.
javax.faces.converter.DateTimeConverter.DATETIME_detail={2}: ''{0}'' could not be understood as a date and time. Example: {1}
javax.faces.converter.DateTimeConverter.PATTERN_TYPE={1}: A 'pattern' or 'type' attribute must be specified to convert the value ''{0}''.
javax.faces.converter.DoubleConverter.DOUBLE={2}: ''{0}'' must be a number consisting of one or more digits.
javax.faces.converter.DoubleConverter.DOUBLE_detail={2}: ''{0}'' must be a number between 4.9E-324 and 1.7976931348623157E308 Example: {1}
javax.faces.converter.EnumConverter.ENUM={2}: ''{0}'' must be convertible to an enum.
javax.faces.converter.EnumConverter.ENUM_detail={2}: ''{0}'' must be convertible to an enum from the enum that contains the constant ''{1}''.
javax.faces.converter.EnumConverter.ENUM_NO_CLASS={1}: ''{0}'' must be convertible to an enum from the enum, but no enum class provided.
javax.faces.converter.EnumConverter.ENUM_NO_CLASS_detail={1}: ''{0}'' must be convertible to an enum from the enum, but no enum class provided.
javax.faces.converter.FloatConverter.FLOAT={2}: ''{0}'' must be a number consisting of one or more digits.
javax.faces.converter.FloatConverter.FLOAT_detail={2}: ''{0}'' must be a number between 1.4E-45 and 3.4028235E38 Example: {1}
javax.faces.converter.IntegerConverter.INTEGER={2}: ''{0}'' must be a number consisting of one or more digits.
javax.faces.converter.IntegerConverter.INTEGER_detail={2}: ''{0}'' must be a number between-2147483648 and 2147483647 Example: {1}
javax.faces.converter.LongConverter.LONG={2}: ''{0}'' must be a number consisting of one or more digits.
javax.faces.converter.LongConverter.LONG_detail={2}: ''{0}'' must be a number between -9223372036854775808 to 9223372036854775807 Example: {1}
javax.faces.converter.NumberConverter.CURRENCY={2}: ''{0}'' could not be understood as a currency value.
javax.faces.converter.NumberConverter.CURRENCY_detail={2}: ''{0}'' could not be understood as a currency value. Example: {1}
javax.faces.converter.NumberConverter.PERCENT={2}: ''{0}'' could not be understood as a percentage.
javax.faces.converter.NumberConverter.PERCENT_detail={2}: ''{0}'' could not be understood as a percentage. Example: {1}
javax.faces.converter.NumberConverter.NUMBER={2}: ''{0}'' is not a number.
javax.faces.converter.NumberConverter.NUMBER_detail={2}: ''{0}'' is not a number. Example:{1}
javax.faces.converter.NumberConverter.PATTERN={2}: ''{0}'' is not a number pattern.
javax.faces.converter.NumberConverter.PATTERN_detail={2}: ''{0}'' is not a number. pattern. Example: {1}
javax.faces.converter.ShortConverter.SHORT={2}: ''{0}'' must be a number consisting of one or more digits.
javax.faces.converter.ShortConverter.SHORT_detail={2}: ''{0}'' must be a number between -32768 and 32767 Example: {1}
javax.faces.converter.STRING={1}: Could not convert ''{0}'' to a string.
javax.faces.validator.DoubleRangeValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of "{0}"
javax.faces.validator.DoubleRangeValidator.MINIMUM={1}: Validation Error: Value is less than allowable minimum of "{0}"
javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE={2}: Validation Error:Specified attribute is not between the expected values of {0} and {1}.
javax.faces.validator.DoubleRangeValidator.TYPE={0}: Validation Error: Value is not of the correct type
javax.faces.validator.LengthValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of "{0}"
javax.faces.validator.LengthValidator.MINIMUM={1}: Validation Error: Value is less than allowable minimum of "{0}"
javax.faces.validator.LongRangeValidator.MAXIMUM={1}: Validation Error: Value is greater than allowable maximum of "{0}"
javax.faces.validator.LongRangeValidator.MINIMUM={1}: Validation Error Value is less than allowable minimum of "{0}"
javax.faces.validator.LongRangeValidator.NOT_IN_RANGE={2}: Validation Error: Specified attribute is not between the expected values of {0} and {1}.
javax.faces.validator.LongRangeValidator.TYPE={0}: Validation Error: Value is not of the correct type The following message keys are deprecated:
javax.faces.validator.NOT_IN_RANGE=Specified attribute is not between the expected values of{0} and {1}
上面这些错误消息的key的含义都非常直观,相信读者可以很容易理解它们。如果要覆盖标准的错误消息,可以通过提供自己的错误消息资源文件来实现。例如,提供如下错误消息资源文件。
程序清单:codes\03\3.3\CustomErrorMessage\WEB-INF\src\crazyitMessages.properties
javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}''不是一个有效的日期! javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' 不是一个有效的日期!应按如下格式输入: {1} javax.faces.converter.DateTimeConverter.TIME={2}: ''{0}'' 不是一个有效的时间! javax.faces.converter.DateTimeConverter.TIME_detail={2}: ''{0}'' 不是一个有效的时间!应按如下格式输入: {1} javax.faces.converter.DateTimeConverter.DATETIME={2}: ''{0}'' 不是一个有效的日期时间! javax.faces.converter.DateTimeConverter.DATETIME_detail={2}:''{0}'' 不是一个有效的日期时间!应按如下格式输入: {1}
使用native2ascii.exe工具处理上面消息资源文件,处理后生成的新文件名为“crazyitMessages_zh_CN.properties”。提供了上面消息资源文件之后,还需要在faces-config.xml文件中配置加载它,并配置系统支持的语言、国家Locale。下面是我们在faces-config.xml文件中增加的配置片段。
程序清单:codes\03\3.3\CustomErrorMessage\WEB-INF\struts-config.xml
<application>
<!-- 配置自定义错误消息资源 -->
<message-bundle>crazyitMessages</message-bundle>
<!-- 配置该应用所支持的语言、国家Locale -->
<locale-config>
<default-locale>en_US</default-locale>
<supported-locale>zh_CN</supported-locale>
</locale-config>
</application>
上面配置片段中粗体字代码指定系统将使用baseName为crazyitMessages的资源文件作为错误消息,因此如果需要系统支持多个国家、语言系统,那就应该为多个国家提供相应的资源文件。例如,本应用提供了crazyitMessages_zh_CN.properties(简体中文)、crazyitMessages_en_US.properties(美式英语)两份文件。
经过上面配置之后,如果用户添加用户时输入的生日不符合规则,将看到如图3.20所示的页面。
图3.20 自定义错误消息
除此之外,还可以为<h:inputText…/>标签添加converterMessage属性来指定类型转换失败后的错误消息。converterMessage属性支持使用表达式语言,因此它既可以直接指定固定的字符串,也可以通过表达式语言来指定国际化消息。
例如,我们把该页面代码改为如下形式。
程序清单:codes\03\3.3\AssignErrorMessage\WEB-INF\struts-config.xml
<h:form prependId="false">
用户名:<h:inputText value="#{userBean.name}"/><br/>
生日:<h:inputText value="#{userBean.birthday}" id="birthday"
converterMessage="您输入的生日格式无效!">
<!-- 指定用户按指定格式输入日期、时间 -->
<f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss"/>
</h:inputText>(请按yyyy-MM-dd HH:mm:ss格式输入)
<!-- 指定输出birthday组件的值类型转换失败、校验失败的错误信息 -->
<h:message for="birthday" style="color:red;font-weight:bold"/><br/>
<h:commandButton value="添加" action="#{userBean.add}"/>
</h:form>
上面页面代码中通过converterMessage属性临时指定了转换失败的错误提示信息——这条错误消息的等级最高,它会覆盖其他错误消息。除此之外,我们还为<h:message…/>标签指定了CSS样式用于重定义错误消息的外观。
如果用户输入的生日转换失败,将会看到如图3.21所示的页面。
图3.21 临时指定的错误消息
从上面介绍可以看出,JSF应用中通过converterMessage属性临时指定错误消息的覆盖等级最高,该属性指定的错误消息会覆盖其他所有的错误消息。