13. 视角渲染
本章将向您展示如何使用视图状态元素用于渲染流程中的视图。
13.1. 定义观点状态
这视图状态element 定义了流程中的一个步骤,渲染视图并等待用户事件恢复,具体如下:
<view-state id="enterBookingDetails">
<transition on="submit" to="reviewBooking" />
</view-state>
按惯例,a视图状态将其 ID 映射到流所在目录中的视图模板。
例如,前述例子中的状态可能生成/网-信息/酒店/预订/enterBookingDetails.xhtml如果流本身位于/WEB-INF/酒店/预订目录。
下图展示了示例目录结构,包含视图和其他资源,如消息包,与其流定义共址:
13.2. 指定视图标识符
你可以使用视图属性以明确指定要渲染视图的ID。
13.2.1. 流量相对视图ID
视图 ID 可以是流工作目录中指向视图资源的相对路径,具体如下:
<view-state id="enterBookingDetails" view="bookingDetails.xhtml">
13.3. 查看范围
一个视图状态分配新的视图范围当它进入时。
你可以在视图状态分配应在状态持续时间内存在的变量。
该作用域适用于在同一视图中处理一系列请求的对象——通常是Ajax请求。
一个视图状态摧毁了它的视图范围当它退出时。
13.3.1. 视图变量分配
你可以使用VARtag 来声明一个视图变量。
与流量变量类似,任何@Autowired当视图状态恢复时,引用会自动恢复。
以下列表声明了一个视图变量:
<var name="searchCriteria" class="com.mycompany.myapp.hotels.SearchCriteria" />
13.3.2. 赋值视图范围变量
你可以使用渲染时在视图渲染前,从动作结果中赋予变量,具体如下:
<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>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>
13.5. 与模型的绑定
你可以使用型属性声明视图绑定对象的模型。
该属性通常与渲染数据控制(如表单)的视图一起使用。
它允许从模型对象的元数据驱动表单、数据绑定和验证行为。
以下示例声明enterBooking详情控预订型:
<view-state id="enterBookingDetails" model="booking">
模型可以是任何可访问范围中的对象,例如flowScope(流控镜)或视图范围.
指定一个型当视图事件发生时,会触发以下行为:
-
视图到模型绑定。在查看回贴时,用户输入值会绑定为你的模型对象属性。
-
模型验证。绑定后,如果模型对象需要验证,就会调用该验证逻辑。
对于能够驱动视图状态转换生成的流程事件,模型绑定必须成功完成。 如果模型绑定失败,视图会重新渲染,允许用户修改编辑内容。
13.6. 进行型号转换
当使用请求参数填充模型(通常称为数据绑定)时,需要类型转换以解析基于字符串的请求参数值,然后再设置目标模型属性。 默认类型转换适用于许多常见的 Java 类型,如数字、原语、枚举和日期。 用户还可以注册自己的类型转换逻辑,用于用户自定义类型并覆盖默认转换器。
13.6.2. 升级至 Spring 3 类型转换与格式化
这在实际应用中意味着什么?现有应用很可能注册了自己的类型转换器org.springframework.binding.convert.converters.Converter通过DefaultConversionService以春季装订版形式提供。
这些转换器可以继续像以前一样注册。
它们被改编为Spring通用转换器类型并注册了Springorg.springframework.core.convert.ConversionService实例。
换句话说,现有转换器通过Spring的类型转换服务被调用。
唯一的例外是命名转换器,你可以参考捆绑一个元素视图状态如下:
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绑定DefaultConversionService不再记录任何默认转换器。
相反,Web Flow 现在依赖于 Spring 中的默认类型转换器和格式化器。
总之,Spring 类型的转换和格式化现在几乎完全用于 Web Flow。 虽然现有应用应无需更改即可运行,但我们鼓励我们朝着统一 Spring MVC 和 Spring Web Flow 部分的类型转换需求迈进。
13.6.3. 配置类型转换与格式化
在春季MVC中,一个实例FormattingConversionService通过自定义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/>
内部,这得益于以下FormattingConversionServiceFactoryBean,注册一套默认的转换器和格式化器。
你可以通过转换服务属性,具体如下:
<mvc:annotation-driven conversion-service="applicationConversionService" />
在 Web Flow 中,Spring绑定的一个实例DefaultConversionService,不注册任何转换器,会自动创建。
相反,它委派给FormattingConversionService所有类型转换需求都应使用实例。
默认情况下,这并不相同FormattingConversionService实例,就像春季中使用的那种。
不过,在你开始注册自己的格式化器之前,这并没有实际意义。
你可以自定义DefaultConversionService在 Web Flow 中通过 flow-builder-services 元素使用,具体如下:
<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"> -
连接网络流
DefaultConversionService同一个applicationConversionService春季MVC中使用的豆子:<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绑定转炉类型通过defaultConversionService.
13.6.4. 处理Spring Type Conversion与格式化
`` 一个重要的概念是类型转换器和格式化器的区别。
Spring 中的类型转换器,提供于org.springframework.core,用于任意两个对象类型之间的通用类型转换。
除了最简单的转炉类型,另外两个接口是转换器工厂和通用转换器.
春季的Formatters,提供于org.springframework.context,具有更专门的目的——表示对象实例字符串实例。
这福尔马特接口扩展了打印机和解析 器用于转换对象转给字符串并且转成字符串变成了对象.
网页开发者可能会发现福尔马特界面最相关,因为它符合Web应用的类型转换需求。
对象到对象的转换是更具体的对象到字符串转换的推广。
事实上Formatters注册为通用转换器类型与斯普林氏通用转换服务使它们等同于任何其他转换器。 |
13.7. 抑制束缚
你可以使用捆属性用于抑制特定视图事件的模型绑定和验证。
以下示例在取消事件发生:
<view-state id="enterBookingDetails" model="booking">
<transition on="proceed" to="reviewBooking">
<transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>
13.8. 明确规定绑定
你可以使用粘结 剂元素用于配置用于应用数据绑定的具体模型属性集合。
这可以限制每个视图的“允许字段”集合。
不使用该功能可能导致安全问题,具体取决于应用域和实际用户,因为默认情况下,如果未指定绑定器元素,模型的所有公共属性都可以被视图绑定数据。
相比之下,当粘结 剂元素被指定,仅允许显式配置的绑定。
以下示例使用了一个粘结 剂元素:
<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>
每个绑定还可以应用转换器,以自定义方式格式化模型属性值以供显示。
如果没有指定转换器,则使用该模型属性类型的默认转换器。
以下示例展示了两个捆绑元素转炉属性:
<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>
在前面的例子中,短约会转换器绑定于入住日期和结账日期性能。
你可以在应用中注册自定义转换器转换服务.
每个绑定还可以执行必要的检查,如果用户提供的值在表单后期为空,则会触发验证错误,具体如下:
<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 豆验证注释,既能编程强制执行,也能声明式地执行此类约束。
13.9.1. JSR-303 豆子验证
Web Flow 内置支持 JSR-303 Beans验证 API,基于 Spring MVC 提供的等效支持。
要启用 JSR-303 验证,请将 flow builder-services 配置为 Spring MVCLocalValidatorFactoryBean如下:
<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豆验证与按惯例验证(下一节解释)并不互斥。 换句话说,Web Flow 应用了所有可用的验证机制。
部分验证
JSR-303 Beans验证支持通过验证组进行部分验证。 以下示例定义了部分验证:
@NotNull
@Size(min = 2, max = 30, groups = State1.class)
private String name;
在流定义中,你可以在视图状态或转换上指定验证提示,这些提示会被解析到验证组。 以下示例定义了验证提示:
<view-state id="state1" model="myModel" validation-hints="'group1,group2'">
这验证提示属性是一个表达式,在前述示例中,其解析为逗号分隔字符串包含两个提示:第一组和第二组.一个验证提示解析器用于解决这些提示。
这BeanValidationHintResolver默认情况下,会尝试将这些字符串解析为基于类的 BEAN 验证组。
为此,它会在模型或其父模型中寻找匹配的内部类型。
例如,给定org.example.MyModel具有内类型第一组和第二组,只需提供简单的类型名即可——即,第一组和第二组.
你也可以提供完全合格的类型名称。
一个取值为默认值具有特殊含义,并被翻译为 Bean Validation 中的默认验证组:jakarta.validation.groups.Default.
你可以自定义配置验证提示解析器,如有必要,通过验证提示解析器的属性流量-构建器-服务元素如下:
<webflow:flow-registry flow-builder-services="flowBuilderServices" />
<webflow:flow-builder-services id="flowBuilderServices" validator=".." validation-hint-resolver=".." />
13.9.2. 程序验证
程序化进行模型验证有两种方式。
第一种是在你的模型对象中实现验证逻辑。
第二种是实现外部验证器.
两种方式都能为你提供一个验证上下文用于记录错误信息并访问当前用户的信息。
实施模型验证方法
在模型对象中定义验证逻辑是验证其状态的最简单方式。
一旦此类逻辑按照Web流程的惯例结构化,Web流程会自动调用该逻辑。视图状态后回溯生命周期。
Web Flow 约定要求你通过以下方式构建模型验证逻辑视图状态,允许你验证该视图上可编辑的模型属性子集。
为此,创建一个名为validate${state}哪里${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());
}
}
}
在前面的例子中,当转移在enterBooking详情 视图状态也就是编辑预订模型中,Web Flow 会自动调用validEnterBookingDetails(ValidationContext)除非该过渡期的验证被抑制。
以下示例展示了这样的视图状态:
<view-state id="enterBookingDetails" model="booking">
<transition on="proceed" to="reviewBooking">
</view-state>
你可以定义任意数量的验证方法。
通常,流程会在一系列视图上编辑模型。
在这种情况下,你会为每个方法定义一个验证方法视图状态验证需要运行。
实现验证器
第二种程序验证方式是定义一个独立的对象,称为验证器,用于验证你的模型对象。
为此,首先创建一个名为 Pattern 的类${model}Validator哪里${model}是模型表达式的大写形式,例如预订.
然后定义一个名为validate${state}哪里${state}是你的ID视图状态如enterBooking详情.
该职业随后应作为春季班部署。
可以定义任意数量的验证方法。
以下示例定义了这样的验证器:
@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());
}
}
}
在前面的例子中,当转移在enterBooking详情 视图状态也就是编辑预订模型中,Web Flow 会自动调用validateEnterBookingDetails(booking, ValidationContext)除非该过渡期的验证被抑制。
验证者也可以接受 Spring MVC错误该对象是调用现有 Spring 验证程序所必需的。
验证者必须注册为春豆,使用以下${model}Validator命名规范,将被自动检测并调用。
在前述示例中,Spring类路径扫描会检测到@Component并自动将其注册为名为bookingValidator.
然后,任何时候预订模型需要验证,这个bookingValidator实例会为你调用。
默认验证方法
验证类还可以定义一个名为驗證按照惯例,不与任何特定的对象关联视图状态.
以下示例定义了此类方法:
@Component
public class BookingValidator {
public void validate(Booking booking, ValidationContext context) {
//...
}
}
在前面的代码示例中,驗證每次有 类型的模型时,方法都会被调用预订是验证的(除非在该转换中被抑制了验证)。如有需要,默认方法也可以在已有的状态特定方法之外调用。
请考虑以下例子:
@Component
public class BookingValidator {
public void validate(Booking booking, ValidationContext context) {
//...
}
public void validateEnterBookingDetails(Booking booking, ValidationContext context) {
//...
}
}
在前面的代码示例中,验证入订详情方法称为第一。
默认驗證方法称为Next。
13.9.3. 该验证上下文接口
一个验证上下文让你得到一个消息上下文用于在验证期间录制消息。
它还会暴露当前用户的信息,比如被信号的用户事件以及当前用户的主要身份。
你可以利用这些信息根据界面中激活的按钮或链接,或认证对象来定制验证逻辑。
请参见API Javadoc验证上下文更多信息请见。
13.10. 抑制验证
你可以使用驗證属性以抑制针对特定视图事件的模型验证,具体如下:
<view-state id="chooseAmenities" model="booking">
<transition on="proceed" to="reviewBooking">
<transition on="back" to="enterBookingDetails" validate="false" />
</view-state>
在前例中,数据绑定仍然发生在返回但验证被抑制。
13.11. 定义视图转换
你可以定义一个或多个过渡处理视图中可能发生的用户事件的元素。
过渡可能会带用户切换到另一个视图,或者执行一个动作并重新渲染当前视图。
在处理Ajax事件时,过渡还可以请求渲染视图的部分(称为“片段”)。
最后,你还可以定义“全局”过渡,这些过渡在所有视图中共享。
以下章节将讨论如何实现视图切换。
13.11.1. 过渡行动
一个视图状态过渡可以在奔跑前调用一个或多个动作。
这些作可能会返回错误结果,以防止跃迁脱离电流视图状态.
如果出现错误结果,视图会重新渲染,并应向用户显示相应的消息。
如果转移作调用的是普通的 Java 方法,被调用的方法可能会返回一个布尔值,其值(true或false)表示是否应进行转换或阻止运行。
方法也可以返回字符串其中字面值为成功,是的或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. 全球转型
你可以用flow的全球转型元素创建适用于所有视图的过渡。
全局过渡通常用于处理布局中的全局菜单链接。
以下示例定义了全球转型元素:
<global-transitions>
<transition on="login" to="login" />
<transition on="logout" to="logout" />
</global-transitions>
13.12. 信息处理
春季网络流消息上下文是一个用于在流程执行过程中记录消息的API。你可以在上下文中添加纯文本消息,也可以添加由Spring解析的国际消息消息源. 消息可通过视图渲染,并能在流执行重定向后自动存活。提供了三种不同的消息严重度:信息,警告和错误. 此外,一个方便的消息构建器存在于流畅构建消息的目的。
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访问的消息包中消息源. 要创建流专用消息包,定义messages.properties在你的流程目录中。创建一个默认文件messages.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
在视图或流程中,你还可以通过以下方式访问消息资源resourceBundleEL变量,具体如下:
<h:outputText value="#{resourceBundle.reservationConfirmation}" />
13.12.4. 理解系统生成消息
Web Flow 本身在多个地方生成消息以显示给用户。其中一个重要环节是视图到模型数据绑定时。当发生绑定错误(如类型转换错误)时,Web Flow 将该错误映射到自动从资源包中检索的消息。为了查找要显示的消息,Web Flow 尝试包含绑定错误代码和目标属性名称的资源键。
举例来说,考虑对入住日期的属性预订对象。 假设用户输入了一个字母字符串。此时会引发类型转换错误。Web Flow 映射类型不匹配通过先查询你的资源包,查找以下密钥的消息,给消息做错误代码:
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. 弹出窗口显示
你可以使用弹出属性以在模态弹出对话框中渲染视图,具体如下:
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
当使用 Web Flow 配合 Spring Javascript 库时,弹窗显示无需客户端代码。Web Flow 向客户端发送响应,请求从弹窗重定向到视图,客户端会响应该请求。