视图技术

Spring WebFlux 中视图技术的用法是可插入的。您决定使用 Thymeleaf、FreeMarker 还是其他视图技术主要是一个配置更改的问题。本章涵盖了与 Spring WebFlux 集成的视图技术。我们假设您已经熟悉 视图解析

Thymeleaf

Thymeleaf 是一款现代的服务器端 Java 模板引擎,它强调自然 HTML 模板,可以通过双击在浏览器中预览,这对于 UI 模板的独立工作(例如,由设计人员进行)非常有帮助,而无需运行服务器。Thymeleaf 提供了一套广泛的功能,并且正在积极开发和维护。有关更完整的介绍,请参阅 Thymeleaf 项目主页。

Thymeleaf 与 Spring WebFlux 的集成由 Thymeleaf 项目管理。配置涉及一些 bean 声明,例如 SpringResourceTemplateResolverSpringWebFluxTemplateEngineThymeleafReactiveViewResolver。有关更多详细信息,请参阅 Thymeleaf+Spring 和 WebFlux 集成 公告

FreeMarker

Apache FreeMarker 是一个模板引擎,用于生成从 HTML 到电子邮件等任何类型的文本输出。Spring Framework 具有内置集成,可将 Spring WebFlux 与 FreeMarker 模板配合使用。

视图配置

以下示例展示了如何将 FreeMarker 配置为视图技术

  • Java

  • Kotlin

@Configuration
@EnableWebFlux
public class WebConfig 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
@EnableWebFlux
class WebConfig : 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 上设置适当的 bean 属性,将 FreeMarker 的“设置”和“共享变量”直接传递给 FreeMarker Configuration 对象(由 Spring 管理)。freemarkerSettings 属性需要一个 java.util.Properties 对象,freemarkerVariables 属性需要一个 java.util.Map。以下示例展示了如何使用 FreeMarkerConfigurer

  • Java

  • Kotlin

@Configuration
@EnableWebFlux
public class WebConfig 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
@EnableWebFlux
class WebConfig : 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 中的相同功能,并提供了其他便利宏以生成表单输入元素本身。

绑定宏

一套标准宏保存在 FreeMarker 的 spring-webflux.jar 文件中,因此它们始终可供适当配置的应用程序使用。

Spring 模板库中定义的一些宏被认为是内部的(私有的),但宏定义中不存在此类范围,使得所有宏对调用代码和用户模板可见。以下部分仅关注您需要直接从模板中调用的宏。如果您希望直接查看宏代码,则该文件名为 spring.ftl,位于 org.springframework.web.reactive.result.view.freemarker 包中。

有关绑定支持的更多详细信息,请参阅 Spring MVC 的 简单绑定

表单宏

有关 Spring 对 FreeMarker 模板的表单宏支持的详细信息,请参阅 Spring MVC 文档的以下部分。

脚本视图

Spring Framework 内置了将 Spring WebFlux 与任何可以在 JSR-223 Java 脚本引擎上运行的模板库集成的功能。下表显示了我们在不同脚本引擎上测试过的模板库

脚本库 脚本引擎

Handlebars

Nashorn

Mustache

Nashorn

React

Nashorn

EJS

Nashorn

ERB

JRuby

字符串模板

Jython

Kotlin 脚本模板

Kotlin

集成任何其他脚本引擎的基本规则是它必须实现 ScriptEngineInvocable 接口。

要求

您需要在类路径上拥有脚本引擎,其详细信息因脚本引擎而异

  • Java 8+ 提供了 Nashorn JavaScript 引擎。强烈建议使用可用的最新更新版本。

  • 应将 JRuby 添加为 Ruby 支持的依赖项。

  • 应将 Jython 添加为 Python 支持的依赖项。

  • 应添加 org.jetbrains.kotlin:kotlin-script-util 依赖项和包含 org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory 行的 META-INF/services/javax.script.ScriptEngineFactory 文件以支持 Kotlin 脚本。有关更多详细信息,请参阅 此示例

您需要拥有脚本模板库。对于 JavaScript,可以通过 WebJars 来实现。

脚本模板

您可以声明一个 ScriptTemplateConfigurer bean 以指定要使用的脚本引擎、要加载的脚本文件、调用以呈现模板的函数,等等。以下示例使用 Mustache 模板和 Nashorn JavaScript 引擎

  • Java

  • Kotlin

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.scriptTemplate();
	}

	@Bean
	public ScriptTemplateConfigurer configurer() {
		ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
		configurer.setEngineName("nashorn");
		configurer.setScripts("mustache.js");
		configurer.setRenderObject("Mustache");
		configurer.setRenderFunction("render");
		return configurer;
	}
}
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {

	override fun configureViewResolvers(registry: ViewResolverRegistry) {
		registry.scriptTemplate()
	}

	@Bean
	fun configurer() = ScriptTemplateConfigurer().apply {
		engineName = "nashorn"
		setScripts("mustache.js")
		renderObject = "Mustache"
		renderFunction = "render"
	}
}

render 函数使用以下参数调用

  • String template:模板内容

  • Map model:视图模型

  • RenderingContext renderingContextRenderingContext,可访问应用程序上下文、区域设置、模板加载器和 URL(5.0 起)

Mustache.render() 本机兼容此签名,因此可以直接调用它。

如果模板技术需要一些自定义,可以提供实现自定义渲染函数的脚本。例如,Handlerbars 在使用模板之前需要编译它们,并且需要polyfill来模拟服务器端脚本引擎中不可用的某些浏览器功能。以下示例显示如何设置自定义渲染函数

  • Java

  • Kotlin

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.scriptTemplate();
	}

	@Bean
	public ScriptTemplateConfigurer configurer() {
		ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
		configurer.setEngineName("nashorn");
		configurer.setScripts("polyfill.js", "handlebars.js", "render.js");
		configurer.setRenderFunction("render");
		configurer.setSharedEngine(false);
		return configurer;
	}
}
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {

	override fun configureViewResolvers(registry: ViewResolverRegistry) {
		registry.scriptTemplate()
	}

	@Bean
	fun configurer() = ScriptTemplateConfigurer().apply {
		engineName = "nashorn"
		setScripts("polyfill.js", "handlebars.js", "render.js")
		renderFunction = "render"
		isSharedEngine = false
	}
}
在使用非线程安全脚本引擎与未针对并发性设计的模板库(例如在 Nashorn 上运行的 Handlebars 或 React)时,需要将 sharedEngine 属性设置为 false。在这种情况下,由于此错误,需要使用 Java SE 8 更新 60,但无论如何,通常建议使用最新的 Java SE 修补程序版本。

polyfill.js 仅定义 Handlebars 正确运行所需的 window 对象,如下代码段所示

var window = {};

此基本 render.js 实现会在使用模板之前编译模板。一个生产就绪的实现还应该存储和重用缓存的模板或预编译的模板。这可以在脚本端完成,以及您需要的任何自定义(例如管理模板引擎配置)。以下示例显示如何编译模板

function render(template, model) {
	var compiledTemplate = Handlebars.compile(template);
	return compiledTemplate(model);
}

查看 Spring Framework 单元测试,Java资源,了解其他配置示例。

JSON 和 XML

出于内容协商目的,根据客户端请求的内容类型,能够在使用 HTML 模板渲染模型或以其他格式(例如 JSON 或 XML)渲染模型之间进行交替非常有用。为了支持这样做,Spring WebFlux 提供了 HttpMessageWriterView,您可以使用它插入任何可用的编解码器spring-web,例如 Jackson2JsonEncoderJackson2SmileEncoderJaxb2XmlEncoder

与其他视图技术不同,HttpMessageWriterView 不需要 ViewResolver,而是配置为默认视图。您可以配置一个或多个此类默认视图,包装不同的 HttpMessageWriter 实例或 Encoder 实例。在运行时使用与请求的内容类型匹配的那个。

在大多数情况下,一个模型包含多个属性。若要确定要序列化的属性,可以使用 HttpMessageWriterView 配置要用于渲染的模型属性的名称。如果模型只包含一个属性,则使用该属性。