13. 渲染视图
本章向您展示如何使用 view-state 元素在流程中渲染视图。
13.1. 定义视图状态
view-state 元素定义了流程中的一个步骤,该步骤渲染视图并等待用户事件以恢复,如下所示:
<view-state id="enterBookingDetails">
<transition on="submit" to="reviewBooking" />
</view-state>
按照惯例,view-state 会将其 ID 映射到流程所在目录中的视图模板。 例如,如果流程本身位于 /WEB-INF/hotels/booking 目录中,前面示例中的状态可能会渲染 /WEB-INF/hotels/booking/enterBookingDetails.xhtml。
下图显示了一个示例目录结构,其中包含视图和其他资源(例如消息包),这些资源与其流定义位于同一位置:
13.2. 指定视图标识符
您可以使用 view 属性显式指定要渲染的视图的 ID。
13.2.1. 流程相对视图ID
视图 ID 可能是相对于流工作目录中视图资源的相对路径,如下所示:
<view-state id="enterBookingDetails" view="bookingDetails.xhtml">
13.3. 视图作用域
A view-state 在进入时会分配一个新的 viewScope。 你可以在 view-state 中引用此作用域,以分配在状态持续期间应该存在的变量。 此作用域对于在来自同一视图的一系列请求(通常是 Ajax 请求)中操作对象非常有用。 A view-state 在退出时会销毁其 viewScope。
13.3.1. 分配视图变量
你可以使用 var 标签来声明一个视图变量。 与流程变量一样,任何 @Autowired 引用都会在视图状态恢复时自动还原。 以下代码清单声明了一个视图变量:
<var name="searchCriteria" class="com.mycompany.myapp.hotels.SearchCriteria" />
13.3.2. 分配一个 viewScope变量
你可以使用 on-render 标签在视图渲染之前从操作结果分配一个变量,如下所示:
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>
13.3.3. 在视图作用域中操作对象
视图作用域中的对象通常在来自同一视图的一系列请求中被操作。 在每次渲染之前,列表会在视图作用域中更新。 异步事件处理器修改当前数据页面,然后请求重新渲染搜索结果片段。 以下示例展示了如何对搜索结果列表进行分页:
<view-state id="searchResults">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" />
</on-render>
<transition on="next">
<evaluate expression="searchCriteria.nextPage()" />
<render fragments="searchResultsFragment" />
</transition>
<transition on="previous">
<evaluate expression="searchCriteria.previousPage()" />
<render fragments="searchResultsFragment" />
</transition>
</view-state>
13.4. 运行渲染操作
on-render 元素在视图渲染之前运行一个或多个操作。 渲染操作在初始渲染以及任何后续刷新(包括视图的任何部分重新渲染)时都会运行。 以下清单定义了一个 on-render 元素:
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>
13.5. 绑定到模型
您可以使用 model 属性声明视图绑定的对象模型。 此属性通常与渲染数据控件(例如表单)的视图结合使用。 它允许通过模型对象上的元数据来驱动表单数据绑定和验证行为。
以下示例声明了一个操作 booking 模型的 enterBookingDetails 状态:
<view-state id="enterBookingDetails" model="booking">
模型可以是任何可访问范围内的对象,例如 flowScope 或 viewScope。 指定 model 会在发生视图事件时触发以下行为:
-
视图到模型的绑定。在视图回发时,用户输入的值会自动绑定到模型对象的属性上。
-
模型验证。在绑定后,如果模型对象需要验证,则会调用该验证逻辑。
为了生成可以驱动视图状态转换的流事件,模型绑定必须成功完成。 如果模型绑定失败,视图将重新渲染,以便用户修订他们的编辑。
13.6. 执行类型转换
当请求参数用于填充模型(通常称为数据绑定)时,在设置目标模型属性之前需要进行类型转换,以解析基于字符串的请求参数值。 许多常见的 Java 类型(如数字、基本类型、枚举和日期)都提供了默认的类型转换。 用户还可以注册自己的类型转换逻辑,以用于用户定义的类型并覆盖默认的转换器。
13.6.2. 升级到 Spring 3 类型转换与格式化
这对现有应用程序而言在实际操作中意味着什么呢?现有的应用程序可能会通过 Spring Binding 中提供的 DefaultConversionService 子类注册它们自己类型为 org.springframework.binding.convert.converters.Converter 的转换器。这些转换器可以像以前一样继续注册。它们已经被适配为 Spring 的 GenericConverter 类型,并注册到一个 Spring 的 org.springframework.core.convert.ConversionService 实例中。换句话说,现有的转换器会通过 Spring 的类型转换服务来调用。
此规则的唯一例外是命名转换器,您可以从 view-state 中的 binding 元素引用它们,如下所示:
public class ApplicationConversionService extends DefaultConversionService {
public ApplicationConversionService() {
addDefaultConverters();
addDefaultAliases();
addConverter("customConverter", new CustomConverter());
}
}
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" required="true" converter="customConverter" />
</binder>
</view-state>
命名转换器不受支持,无法与 Spring 框架中的类型转换服务一起使用。 因此,这些转换器未进行适配,仍然保持原有的工作方式。 也就是说,它们不涉及 Spring 的类型转换。 然而,此机制已被弃用,建议应用程序优先使用 Spring 的类型转换和格式化功能。
另请注意,现有的 Spring Binding DefaultConversionService 不再注册任何默认转换器。 相反,Web Flow 现在依赖于 Spring 中的默认类型转换器和格式化程序。
总之,Spring 类型转换和格式化现在几乎专门用于 Web Flow。 尽管现有应用程序应该可以无需任何更改即可运行,但我们鼓励朝着统一 Spring MVC 和 Spring Web Flow 部分的应用程序类型转换需求的方向发展。
13.6.3. 配置类型转换和格式化
在 Spring MVC 中,通过自定义的 MVC 命名空间会自动创建一个 FormattingConversionService 的实例,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<mvc:annotation-driven/>
Internally, that is done with the help of FormattingConversionServiceFactoryBean, which registers a default set of converters and formatters. You can customize the conversion service instance used in Spring MVC through the conversion-service attribute, as follows:
<mvc:annotation-driven conversion-service="applicationConversionService" />
在 Web Flow 中,会自动创建一个 Spring Binding 的实例 DefaultConversionService,该实例不会注册任何转换器。 而是将所有类型转换的需求委托给一个 FormattingConversionService 实例。 默认情况下,这个实例与 Spring 中使用的 FormattingConversionService 实例并不相同。 然而,这在您开始注册自己的格式化程序之前并不会产生实际的影响。
你可以通过 flow-builder-services 元素自定义 Web Flow 中使用的 DefaultConversionService,如下所示:
<webflow:flow-builder-services id="flowBuilderServices" conversion-service="defaultConversionService" />
您可以执行以下操作来注册您自己的格式化程序,以在 Spring MVC 和 Spring Web Flow 中使用:
-
创建一个类来注册您的自定义格式化程序:
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean { @Override protected void installFormatters(FormatterRegistry registry) { // ... } } -
配置此类以在 Spring MVC 中使用:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <mvc:annotation-driven conversion-service="applicationConversionService" /> <!-- Alternatively if you prefer annotations for DI: 1. Add @Component to the factory bean. 2. Add a component-scan element (from the context custom namespace) here. 3. Remove XML bean declaration below. --> <bean id="applicationConversionService" class="somepackage.ApplicationConversionServiceFactoryBean"> -
将 Web Flow 的
DefaultConversionService连接到 Spring MVC 中使用的同一个applicationConversionServicebean:<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" ... /> <webflow:flow-builder-services id="flowBuilderServices" conversion-service="defaultConversionService" ... /> <bean id="defaultConversionService" class="org.springframework.binding.convert.service.DefaultConversionService"> <constructor-arg ref="applicationConversionSevice"/> </bean>
你也可以混合使用。 你可以通过 applicationConversionService 注册新的 Spring Formatter 类型。 你可以通过 defaultConversionService 注册现有的 Spring Binding Converter 类型。
13.6.4. 使用 Spring 类型转换和格式化
`` 理解类型转换器和格式化器之间的区别是一个重要的概念。
Spring框架中的类型转换器,包含在org.springframework.core中,用于任何两种对象类型之间的通用类型转换。 除了最简单的Converter类型外,另外两个接口是ConverterFactory和GenericConverter。
Spring框架中的格式化器,位于org.springframework.context中,具有将Object实例表示为String实例的更专门用途。 Formatter接口扩展了Printer和Parser接口,用于将Object转换为String,并将String转换为Object。
Web 开发人员可能会发现 Formatter 接口最为相关,因为它满足了 Web 应用程序进行类型转换的需求。
对象到对象的转换是更具体的对象到字符串转换的一种泛化。实际上,Formatters 作为 GenericConverter 类型注册到了 Spring 的 GenericConversionService 中,使它们与其他任何转换器等效。 |
13.7. 抑制绑定
您可以使用 bind 属性来抑制特定视图事件的模型绑定和验证。 以下示例在 cancel 事件发生时抑制绑定:
<view-state id="enterBookingDetails" model="booking">
<transition on="proceed" to="reviewBooking">
<transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>
13.8. 明确指定绑定
您可以使用 binder 元素来配置要应用数据绑定的模型属性的确切集合。 这使您可以限制每个视图的“允许字段”集。 如果不使用此功能,可能会导致安全问题,具体取决于应用程序领域和实际用户,因为默认情况下,如果未指定绑定器元素,则模型的所有公共属性都可用于视图的数据绑定。 相比之下,当指定了 binder 元素时,只有显式配置的绑定是被允许的。 以下示例使用了一个 binder 元素:
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="creditCard" />
<binding property="creditCardName" />
<binding property="creditCardExpiryMonth" />
<binding property="creditCardExpiryYear" />
</binder>
<transition on="proceed" to="reviewBooking" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>
每个绑定还可以应用一个转换器,以自定义格式化模型属性值以便以特定方式显示。如果没有指定转换器,则使用该模型属性类型的默认转换器。以下示例展示了两个带有 converter 属性的 binding 元素:
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" converter="shortDate" />
<binding property="checkoutDate" converter="shortDate" />
<binding property="creditCard" />
<binding property="creditCardName" />
<binding property="creditCardExpiryMonth" />
<binding property="creditCardExpiryYear" />
</binder>
<transition on="proceed" to="reviewBooking" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>
在前面的示例中,shortDate 转换器被绑定到 checkinDate 和 checkoutDate 属性。您可以使用应用程序的 ConversionService 注册自定义转换器。
每个绑定还可以应用一个必需的检查,以便在表单回发时,如果用户提供的值为 null,则生成验证错误,如下所示:
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" converter="shortDate" required="true" />
<binding property="checkoutDate" converter="shortDate" required="true" />
<binding property="creditCard" required="true" />
<binding property="creditCardName" required="true" />
<binding property="creditCardExpiryMonth" required="true" />
<binding property="creditCardExpiryYear" required="true" />
</binder>
<transition on="proceed" to="reviewBooking">
<transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>
在前面的示例中,所有绑定都是必需的。 如果一个或多个空白输入值被绑定,将生成验证错误,并且视图会重新渲染并显示这些错误。
13.9. 验证模型
模型验证由针对模型对象指定的约束条件驱动。 Web Flow 支持以编程方式以及通过 JSR-303 Bean Validation 注解声明式地强制执行这些约束条件。
13.9.1. JSR-303 Bean 验证
Web Flow 提供了对 JSR-303 Bean Validation API 的内置支持,基于 Spring MVC 中等效的支持。 要启用 JSR-303 验证,请使用 Spring MVC 的 LocalValidatorFactoryBean 配置 flow-builder-services,如下所示:
<webflow:flow-registry flow-builder-services="flowBuilderServices" />
<webflow:flow-builder-services id="flowBuilderServices" validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
在前面的示例就位后,配置的验证器会在数据绑定后应用于所有模型属性。
请注意,JSR-303 bean 验证和按约定验证(在下一节中解释)并不是互斥的。 换句话说,Web Flow 会应用所有可用的验证机制。
部分验证
JSR-303 Bean Validation 支持通过验证组进行部分验证。 以下示例定义了部分验证:
@NotNull
@Size(min = 2, max = 30, groups = State1.class)
private String name;
在流程定义中,您可以为视图状态或转换指定验证提示,这些提示会被解析为验证组。 以下示例定义了验证提示:
<view-state id="state1" model="myModel" validation-hints="'group1,group2'">
validation-hints 属性是一个表达式,在前面的例子中,它解析为一个逗号分隔的 String,包含两个提示: group1 和 group2。使用 ValidationHintResolver 来解析这些提示。 默认使用的 BeanValidationHintResolver 会尝试将这些字符串解析为基于类的 bean 验证组。 为此,它会在模型或其父级中查找匹配的内部类型。
例如,给定 org.example.MyModel 的内部类型为 Group1 和 Group2,提供简单类型名称即可,即 group1 和 group2。 您还可以提供完全限定的类型名称。
值为 default 的提示具有特殊含义,并在 Bean Validation 中被转换为默认的验证组: jakarta.validation.groups.Default。
你可以通过 validationHintResolver 元素的 flow-builder-services 属性(如果需要)来配置自定义的 ValidationHintResolver,如下所示:
<webflow:flow-registry flow-builder-services="flowBuilderServices" />
<webflow:flow-builder-services id="flowBuilderServices" validator=".." validation-hint-resolver=".." />
13.9.2. 编程式验证
有两种方法可以以编程方式执行模型验证。 第一种是在模型对象中实现验证逻辑。 第二种是实现一个外部的 Validator。 这两种方法都为您提供了一个 ValidationContext 来记录错误消息并访问有关当前用户的信息。
实现模型验证方法
在模型对象中定义验证逻辑是验证其状态的最简单方法。 一旦根据 Web Flow 约定构建了此类逻辑,Web Flow 就会在 view-state 回发生命周期期间自动调用该逻辑。 Web Flow 约定要求您通过 view-state 来构建模型验证逻辑,从而使您可以验证该视图上可编辑的模型属性子集。 为此,请创建一个公共方法,其名称为 validate${state},其中 ${state} 是您希望运行验证的 view-state 的 ID。 以下示例执行模型验证:
public class Booking {
private Date checkinDate;
private Date checkoutDate;
...
public void validateEnterBookingDetails(ValidationContext context) {
MessageContext messages = context.getMessageContext();
if (checkinDate.before(today())) {
messages.addMessage(new MessageBuilder().error().source("checkinDate").
defaultText("Check in date must be a future date").build());
} else if (!checkinDate.before(checkoutDate)) {
messages.addMessage(new MessageBuilder().error().source("checkoutDate").
defaultText("Check out date must be later than check in date").build());
}
}
}
在前面的示例中,当在编辑 Booking 模型的 enterBookingDetails view-state 中触发转换时,Web Flow 会自动调用 validateEnterBookingDetails(ValidationContext) 方法,除非该转换的验证已被禁止。 以下示例展示了这样的 view-state:
<view-state id="enterBookingDetails" model="booking">
<transition on="proceed" to="reviewBooking">
</view-state>
您可以定义任意数量的验证方法。 通常,一个流程会在一系列视图中编辑模型。 在这种情况下,您需要为每个需要运行验证的view-state定义一个验证方法。
实现一个验证器
执行编程验证的第二种方法是定义一个单独的对象,称为验证器,它验证您的模型对象。 为此,首先创建一个类,其名称模式为${model}Validator,其中${model}是模型表达式的首字母大写形式,例如Booking。 然后定义一个公共方法,其名称为validate${state},其中${state}是您的view-state的ID,例如enterBookingDetails。 该类应作为Spring bean部署。 可以定义任意数量的验证方法。 以下示例定义了这样一个验证器:
@Component
public class BookingValidator {
public void validateEnterBookingDetails(Booking booking, ValidationContext context) {
MessageContext messages = context.getMessageContext();
if (booking.getCheckinDate().before(today())) {
messages.addMessage(new MessageBuilder().error().source("checkinDate").
defaultText("Check in date must be a future date").build());
} else if (!booking.getCheckinDate().before(booking.getCheckoutDate())) {
messages.addMessage(new MessageBuilder().error().source("checkoutDate").
defaultText("Check out date must be later than check in date").build());
}
}
}
在前面的示例中,当在编辑 Booking 模型的 enterBookingDetails view-state 中触发转换时,Web Flow 会自动调用 validateEnterBookingDetails(Booking, ValidationContext) 方法,除非该转换的验证已被禁用。
验证器还可以接受一个 Spring MVC Errors 对象,这是调用现有的 Spring 验证器所必需的。
验证器必须作为 Spring 的 bean 注册,使用 ${model}Validator 命名约定,才能被自动检测并调用。 在前面的例子中,Spring 的类路径扫描会检测到 @Component 并自动将其注册为名为 bookingValidator 的 bean。 然后,任何时候当需要验证 booking 模型时,都会为您调用这个 bookingValidator 实例。
默认验证方法
一个验证器类还可以定义一个名为validate的方法,该方法(按照惯例)不与任何特定的view-state相关联。 以下示例定义了这样的方法:
@Component
public class BookingValidator {
public void validate(Booking booking, ValidationContext context) {
//...
}
}
在前面的代码示例中,每次验证类型为 Booking 的模型时都会调用 validate 方法(除非该转换的验证已被抑制)。如果需要,还可以在现有的特定状态方法之外调用默认方法。 请考虑以下示例:
@Component
public class BookingValidator {
public void validate(Booking booking, ValidationContext context) {
//...
}
public void validateEnterBookingDetails(Booking booking, ValidationContext context) {
//...
}
}
在前面的代码示例中,首先调用 validateEnterBookingDetails 方法。 接下来调用默认的 validate 方法。
13.9.3. ValidationContext接口
A ValidationContext 让您能够获取一个 MessageContext,以在验证期间记录消息。 它还公开了当前用户的信息,例如发出的 userEvent 和当前用户的 Principal 身份。 您可以利用这些信息根据 UI 中激活的按钮或链接,或者根据已通过身份验证的用户来定制验证逻辑。 有关详细信息,请参阅 ValidationContext 的 API Javadoc。
13.10. 抑制验证
您可以使用 validate 属性来抑制特定视图事件的模型验证,如下所示:
<view-state id="chooseAmenities" model="booking">
<transition on="proceed" to="reviewBooking">
<transition on="back" to="enterBookingDetails" validate="false" />
</view-state>
在前面的示例中,数据绑定仍然发生在 back 上,但验证被抑制了。
13.11. 定义视图转换
你可以定义一个或多个transition元素来处理可能在视图上发生的用户事件。 转换可能会将用户带到另一个视图,或者它可能会执行某个操作并重新渲染当前视图。 在处理Ajax事件时,转换还可能请求渲染视图的某些部分(称为“片段”)。 最后,你还可以定义在所有视图之间共享的“全局”转换。
以下部分讨论如何实现视图转换。
13.11.1. 转换操作
A view-state 转换在运行之前可以调用一个或多个操作。 这些操作可能会返回一个错误结果,以防止转换离开当前的 view-state。 如果发生错误结果,视图将重新渲染,并应向用户显示适当的消息。
如果转换操作调用了一个普通的 Java 方法,被调用的方法可以返回一个布尔值,其值(true 或 false)表示转换是否应该发生或被阻止运行。 方法还可以返回 String,其中 success、yes 或 true 的字面值表示转换应该发生,而任何其他值则表示相反的含义。 您可以使用这种技术来处理服务层方法抛出的异常。 以下示例调用了一个调用服务并处理异常情况的操作:
<transition on="submit" to="bookingConfirmed">
<evaluate expression="bookingAction.makeBooking(booking, messageContext)" />
</transition>
public class BookingAction {
public boolean makeBooking(Booking booking, MessageContext context) {
try {
bookingService.make(booking);
return true;
} catch (RoomNotAvailableException e) {
context.addMessage(new MessageBuilder().error().
.defaultText("No room is available at this hotel").build());
return false;
}
}
}
当在一个转换上定义了多个操作时,如果其中一个返回错误结果,则集合中剩余的操作将不会被执行。 如果你需要确保一个转换操作的结果不会影响另一个操作的执行,请定义一个单独的转换操作,该操作调用封装了所有操作逻辑的方法。
13.11.2. 全局转换
您可以使用流的 global-transitions 元素来创建适用于所有视图的转换。 全局转换通常用于处理作为布局一部分的全局菜单链接。 以下示例定义了一个 global-transition 元素:
<global-transitions>
<transition on="login" to="login" />
<transition on="logout" to="logout" />
</global-transitions>
13.12. 使用消息
Spring Web Flow的MessageContext 是一个用于在流执行过程中记录消息的API。 您可以将纯文本消息添加到上下文中,也可以添加由Spring MessageSource解析的国际化消息。 消息可以由视图渲染,并且在流执行重定向时会自动保留。 提供了三种不同的消息严重性级别:info、warning 和 error。 此外,还提供了一个便捷的MessageBuilder,用于流畅地构造消息。
13.12.1. 添加纯文本消息
您可以向上下文中添加纯文本消息。 以下示例展示了如何实现:
MessageContext context = ...
MessageBuilder builder = new MessageBuilder();
context.addMessage(builder.error().source("checkinDate")
.defaultText("Check in date must be a future date").build());
context.addMessage(builder.warn().source("smoking")
.defaultText("Smoking is bad for your health").build());
context.addMessage(builder.info()
.defaultText("We have processed your reservation - thank you and enjoy your stay").build());
13.12.2. 添加国际化消息
您可以向上下文中添加国际化的(即本地化的)消息。 以下示例展示了如何实现:
MessageContext context = ...
MessageBuilder builder = new MessageBuilder();
context.addMessage(builder.error().source("checkinDate").code("checkinDate.notFuture").build());
context.addMessage(builder.warn().source("smoking").code("notHealthy")
.resolvableArg("smoking").build());
context.addMessage(builder.info().code("reservationConfirmation").build());
13.12.3. 使用消息包
国际化消息定义在由 Spring MessageSource 访问的消息包中。 要创建特定流程的消息包,请在您的流程目录中定义 messages.properties 文件。 创建一个默认的 messages.properties 文件,并为每个需要支持的额外 Locale 创建一个 .properties 文件。 以下示例定义了一些消息:
#messages.properties
checkinDate=Check in date must be a future date
notHealthy={0} is bad for your health
reservationConfirmation=We have processed your reservation - thank you and enjoy your stay
在视图或流程中,您还可以通过使用 resourceBundle EL 变量来访问消息资源,如下所示:
<h:outputText value="#{resourceBundle.reservationConfirmation}" />
13.12.4. 理解系统生成的消息
Web Flow 本身在多个地方生成要向用户显示的消息。 一个重要的地方是在视图到模型的数据绑定过程中。 当发生绑定错误(例如类型转换错误)时,Web Flow 会将该错误映射到一条消息,这条消息会自动从您的资源包中检索。 为了查找要显示的消息,Web Flow 会尝试使用包含绑定错误代码和目标属性名称的资源键。
作为一个示例,考虑将绑定到 checkinDate 对象的 Booking 属性。 假设用户输入了一个字母字符串。 在这种情况下,会引发类型转换错误。 Web Flow 通过首先查询资源束中具有以下键的消息,将 typeMismatch 错误代码映射到一条消息:
booking.checkinDate.typeMismatch
键的第一部分是模型类的短名称。 键的第二部分是属性名称。 第三部分是错误代码。 这允许在模型属性绑定失败时查找要向用户显示的唯一消息。 这样的消息可能会说:
booking.checkinDate.typeMismatch=The check in date must be in the format yyyy-mm-dd.
如果找不到该形式的资源键,则会尝试一个更通用的键。 此键是错误代码。 属性的字段名称作为消息参数提供,如下所示:
typeMismatch=The {0} field is of the wrong type.
13.13. 显示弹出窗口
你可以使用 popup 属性在模态弹出对话框中渲染视图,如下所示:
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
当将 Web Flow 与 Spring Javascript 库一起使用时,弹出窗口显示不需要客户端代码。 Web Flow 会向客户端发送响应,请求从弹出窗口重定向到视图,客户端会遵守该请求。