视图技术
在Spring WebFlux中,视图的渲染是可插拔的。你是否选择使用Thymeleaf、FreeMarker或其他视图技术,主要取决于配置的更改。本章将介绍与Spring WebFlux集成的各种视图技术。
有关视图渲染的更多信息,请参阅视图分辨率。
Spring WebFlux应用程序的视图位于应用程序的内部信任边界内。视图可以访问应用程序上下文中的bean,因此,我们不建议在那些模板可以被外部来源编辑的应用程序中使用Spring WebFlux的模板支持,因为这可能会带来安全风险。
Thymeleaf
Thymeleaf 是一种现代的服务器端 Java 模板引擎,它强调使用自然的 HTML 模板,这些模板可以通过双击在浏览器中预览,这对于独立开发 UI 模板(例如由设计师进行)非常有用,而无需运行服务器。Thymeleaf 提供了一整套丰富的功能,并且仍在持续积极地开发和维护中。如需更全面的介绍,请访问 Thymeleaf 项目的主页。
Thymeleaf与Spring WebFlux的集成由Thymeleaf项目负责管理。配置涉及几个bean的声明,例如SpringResourceTemplateResolver、SpringWebFluxTemplateEngine和ThymeleafReactiveViewResolver。更多详情,请参阅Thymeleaf+Spring以及WebFlux集成公告。
FreeMarker
Apache FreeMarker 是一个模板引擎,可以生成从 HTML 到电子邮件等各种类型的文本输出。Spring Framework 内置了与 FreeMarker 模板的集成功能,以便与 Spring WebFlux 一起使用。
视图配置
以下示例展示了如何将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 配置
你可以通过在FreeMarkerConfigurer bean上设置相应的属性,直接将FreeMarker的Settings和SharedVariables传递给由Spring管理的FreeMarker Configuration对象。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()))
}
}
有关适用于Configuration对象的设置和变量的详细信息,请参阅FreeMarker文档。
表单处理
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 的表单宏对 FreeMarker 模板支持的详细信息,请参阅 Spring MVC 文档的以下部分。
脚本视图
Spring框架内置了与Spring WebFlux的集成功能,可以用于任何能够在JSR-223 Java脚本引擎上运行的模板库。下表展示了我们在不同脚本引擎上测试过的模板库:
集成任何其他脚本引擎的基本规则是,该引擎必须实现ScriptEngine和Invocable接口。
要求
你需要在类路径(classpath)中包含脚本引擎,不同脚本引擎的具体要求如下:
脚本模板
你可以声明一个ScriptTemplateConfigurerbean来指定要使用的脚本引擎、要加载的脚本文件、调用哪个函数来渲染模板等等。以下示例使用了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"
}
}
render 函数被调用时带有以下参数:
String template:模板内容Map model:视图模型RenderingContext renderingContext:RenderingContext,它提供了对应用程序上下文、本地化设置(locale)、模板加载器(template loader)以及URL的访问权限
HTML片段
HTMX和Hotwire Turbo强调一种“HTML-over-the-wire”的方法,即客户端接收到的服务器更新是以HTML格式而非JSON格式传输的。这种方法能够带来单页应用(SPA)的优点,而无需编写大量甚至任何JavaScript代码。如需全面了解或了解更多信息,请访问它们的官方网站。
在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(服务器发送的事件,server-sent events)进行流式更新。控制器可以使用Flux<Fragment>来创建FragmentsRendering,或者使用任何其他可以通过ReactiveAdapterRegistry适配到Reactive Streams Publisher的 reactive producer。也可以不使用FragmentsRendering包装器,直接返回Flux<Fragment>。
JSON和XML
与其他视图技术不同,HttpMessageWriterView不需要ViewResolver,而是被配置为默认视图。你可以配置一个或多个这样的默认视图,它们可以包装不同的HttpMessageWriter实例或Encoder实例。在运行时,会使用与请求的内容类型相匹配的那个视图。
在大多数情况下,一个模型包含多个属性。要确定序列化哪个属性,您可以使用模型的属性名称来配置HttpMessageWriterView以用于渲染。如果模型只包含一个属性,则使用该属性。