视图技术
Spring WebFlux中的视图渲染是可插拔的。无论您选择使用Thymeleaf、FreeMarker还是其他某种视图技术,主要都是通过配置更改来决定的。本章将介绍与Spring WebFlux集成的视图技术。
关于视图渲染的更多上下文信息,请参见视图解析。
| Spring WebFlux应用程序的视图位于应用内部的信任边界内。视图可以访问应用上下文中的bean,因此我们不建议在模板可被外部源编辑的应用程序中使用Spring WebFlux的模板支持,因为这可能带来安全风险。 |
百里叶
Thymeleaf 是一个现代的服务器端 Java 模板引擎,它强调自然的 HTML 模板,可以通过双击在浏览器中预览,这对于独立进行 UI 模板工作(例如,由设计师)非常有帮助,而无需运行服务器。Thymeleaf 提供了广泛的功能,并且正在积极开发和维护中。有关更完整的介绍,请参阅 Thymeleaf 项目主页。
Thymeleaf与Spring WebFlux的集成由Thymeleaf项目管理。配置涉及一些bean声明,例如
SpringResourceTemplateResolver, SpringWebFluxTemplateEngine, 和
ThymeleafReactiveViewResolver. 有关更多详细信息,请参阅
Thymeleaf+Spring 和WebFlux集成的
公告。
自由标记
Apache FreeMarker 是一个模板引擎,用于生成从 HTML 到电子邮件等各种类型的文本输出。Spring 框架内置了使用 Spring WebFlux 和 FreeMarker 模板的集成。
查看配置
以下示例展示了如何将FreeMarker配置为视图技术:
-
Java
-
Kotlin
@Configuration
public class WebConfiguration implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
}
// Configure FreeMarker...
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("classpath:/templates/freemarker");
return configurer;
}
}
@Configuration
class WebConfiguration : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.freeMarker()
}
// Configure FreeMarker...
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("classpath:/templates/freemarker")
}
}
您的模板需要存储在由FreeMarkerConfigurer指定的目录中,如前面的例子所示。根据前面的配置,如果您的控制器返回视图名称welcome,解析器将查找classpath:/templates/freemarker/welcome.ftl模板。
FreeMarker 配置
您可以直接将FreeMarker的“设置”和“共享变量”传递给由Spring管理的FreeMarker
Configuration 对象,方法是设置FreeMarkerConfigurer bean的相应bean属性。freemarkerSettings 属性需要一个java.util.Properties 对象,而freemarkerVariables 属性需要一个java.util.Map。以下示例演示了如何使用FreeMarkerConfigurer:
-
Java
-
Kotlin
@Configuration
public class WebConfiguration implements WebFluxConfigurer {
// ...
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
Map<String, Object> variables = new HashMap<>();
variables.put("xml_escape", new XmlEscape());
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("classpath:/templates");
configurer.setFreemarkerVariables(variables);
return configurer;
}
}
@Configuration
class WebConfiguration : WebFluxConfigurer {
// ...
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("classpath:/templates")
setFreemarkerVariables(mapOf("xml_escape" to XmlEscape()))
}
}
请参阅FreeMarker文档,了解适用于Configuration对象的设置和变量的详细信息。
表单处理
Spring 提供了一个标签库供 JSP 使用,其中包含一个 <spring:bind/> 元素。此元素主要用于让表单显示来自表单支持对象的值,并显示来自 web 或业务层中的 Validator 的验证失败结果。Spring 还支持在 FreeMarker 中使用相同的功能,并提供了额外的便利宏来生成表单输入元素。
绑定宏
一组标准的宏在spring-webflux.jar文件中维护,用于FreeMarker,因此它们始终可用于配置适当的应用程序。
Spring模板库中定义的一些宏被认为是内部的(私有的),但宏定义中没有这样的作用域,使得所有宏对调用代码和用户模板都是可见的。以下部分仅集中于你需要直接在模板中调用的宏。如果你想直接查看宏代码,文件名为spring.ftl,位于org.springframework.web.reactive.result.view.freemarker包中。
有关绑定支持的详细信息,请参阅简单绑定以了解Spring MVC。
脚本视图
Spring框架内置了使用Spring WebFlux与任何可以在JSR-223 Java脚本引擎上运行的模板库集成的功能。 下表列出了我们在不同脚本引擎上测试过的模板库:
| 脚本库 | 脚本引擎 |
|---|---|
集成任何其他脚本引擎的基本规则是,它必须实现ScriptEngine和Invocable接口。 |
脚本模板
您可以声明一个ScriptTemplateConfigurer bean来指定要使用的脚本引擎、要加载的脚本文件、用于渲染模板的函数等。以下示例使用了Jython Python引擎:
-
Java
-
Kotlin
@Configuration
public class WebConfiguration implements WebFluxConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.scriptTemplate();
}
@Bean
public ScriptTemplateConfigurer configurer() {
ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
configurer.setEngineName("jython");
configurer.setScripts("render.py");
configurer.setRenderFunction("render");
return configurer;
}
}
@Configuration
class WebConfiguration : WebFluxConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.scriptTemplate()
}
@Bean
fun configurer() = ScriptTemplateConfigurer().apply {
engineName = "jython"
setScripts("render.py")
renderFunction = "render"
}
}
The render 函数的调用参数如下:
-
String template: 模板内容 -
Map model: 视图模型 -
RenderingContext renderingContext: 提供访问应用程序上下文、区域设置、模板加载器和 URL 的RenderingContext
HTML 片段
HTMX 和 Hotwire Turbo 强调了一种通过网络发送HTML的方略,即客户端接收的是HTML形式的服务器更新而非JSON。这使得在无需编写大量甚至任何JavaScript代码的情况下,也能享受到单页面应用(SPA)的好处。欲了解更多详情,请访问它们各自的官方网站。
在Spring WebFlux中,视图渲染通常涉及指定一个视图和一个模型。然而,在HTML-over-the-wire中,一个常见的功能是发送多个HTML片段,浏览器可以使用这些片段更新页面的不同部分。为此,控制器方法可以返回Collection<Fragment>。例如:
-
Java
-
Kotlin
@GetMapping
List<Fragment> handle() {
return List.of(Fragment.create("posts"), Fragment.create("comments"));
}
@GetMapping
fun handle(): List<Fragment> {
return listOf(Fragment.create("posts"), Fragment.create("comments"))
}
同样也可以通过返回专门的类型FragmentsRendering来实现:
-
Java
-
Kotlin
@GetMapping
FragmentsRendering handle() {
return FragmentsRendering.fragment("posts").fragment("comments").build();
}
@GetMapping
fun handle(): FragmentsRendering {
return FragmentsRendering.fragment("posts").fragment("comments").build()
}
每个片段都可以拥有一个独立的模型,并且该模型从请求的共享模型中继承属性。
HTMX和Hotwire Turbo支持通过SSE(服务器发送事件)进行流式更新。
控制器可以使用FragmentsRendering与Flux<Fragment>创建,或者通过ReactiveAdapterRegistry使用任何其他可适应Reactive Streams Publisher的响应式生产者来创建。
直接返回Flux<Fragment>而不使用FragmentsRendering包装器也是可能的。
JSON 和 XML
对于内容协商的目的,能够根据客户端请求的内容类型在使用HTML模板渲染模型和以其他格式(如JSON或XML)之间切换是有用的。为此,Spring WebFlux提供了HttpMessageWriterView,你可以使用它来插入任何可用的编解码器从spring-web,例如JacksonJsonEncoder、JacksonSmileEncoder或Jaxb2XmlEncoder。
与其它视图技术不同,HttpMessageWriterView 不需要 ViewResolver,而是作为默认视图进行配置。您可以配置一个或多个这样的默认视图,封装不同的 HttpMessageWriter 实例或 Encoder 实例。运行时将使用匹配请求内容类型的那一个。
在大多数情况下,模型包含多个属性。要确定要序列化的属性,你可以通过配置HttpMessageWriterView来指定要用于渲染的模型属性名称。如果模型只包含一个属性,则使用该属性。