View Technologies

在 Spring WebFlux 中,视图技术的用途是可插入的。无论你决定使用 Thymeleaf、FreeMarker 还是其他视图技术,主要取决于配置更改。本章涵盖与 Spring WebFlux 集成的视图技术。我们假设你已经熟悉View Resolution

Thymeleaf

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

Thymeleaf 与 Spring WebFlux 的集成由 Thymeleaf 项目管理。配置涉及几个 Bean 声明,例如 SpringResourceTemplateResolverSpringWebFluxTemplateEngine`和 `ThymeleafReactiveViewResolver。有关更多详细信息,请参阅 Thymeleaf+Spring和 WebFlux 集成 announcement

FreeMarker

Apache FreeMarker是用于从 HTML 生成任何种类的文本输出(到电子邮件等)的模板引擎。Spring Framework 已内置集成,以便将 Spring WebFlux 与 FreeMarker 模板一起使用。

View Configuration

以下示例展示了如何将 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 Configuration

你可以通过设置 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()))
	}
}

请参见 FreeMarker 文档,了解设置和变量的详细信息,因为它们适用于 Configuration 对象。

Form Handling

Spring 提供了一个标签库,用于 JSP,其中包括 <spring:bind/> 元素。此元素主要允许表单显示来自表单后备对象的数值,以及显示来自 Web 或业务层中的 Validator 的不成功验证的结果。Spring 在 FreeMarker 中也支持同样的功能,并提供其他方便的宏,用于生成表单输入元素本身。

The Bind Macros

针对 FreeMarker 在 spring-webflux.jar 文件中维护了一组标准宏,因此始终可用于妥善配置的应用程序。

Spring 模板库中定义的某些宏被视为内部(私有)宏,但在宏定义中不存在这种作用域,使所有宏对调用代码和用户模板可见。以下各节仅着重于您需要直接从模板内部调用的宏。如果您希望直接查看宏代码,该文件名为 spring.ftl,位于`org.springframework.web.reactive.result.view.freemarker` 包中。

有关绑定支持的其他详细信息,请参阅 Spring MVC 的Simple Binding

Form Macros

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

Script Views

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

Scripting Library Scripting Engine

Handlebars

Nashorn

Mustache

Nashorn

React

Nashorn

EJS

Nashorn

ERB

JRuby

String templates

Jython

Kotlin Script templating

Kotlin

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

Requirements

您需要在类路径中使用脚本引擎,其详细信息因脚本引擎的不同而有所不同:

  • 在 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 脚本。查看 this example 了解更多详情。

你需要具有脚本模板化库。对于 JavaScript,执行此操作的一种方法是通过 WebJars

Script Templates

您可以声明一个 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 renderingContext:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/view/script/RenderingContext.html[RenderingContext],可用于访问应用程序上下文、语言环境、模板加载器和 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 上运行 Handlebar 或 React 等不适用于并发且专为并发设计的模板化库中使用非线程安全脚本引擎时,设置 sharedEngine 属性为 false 是必需的。在这种情况下,由于 this bug,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 单元测试、https://github.com/spring-projects/spring-framework/tree/main/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/script[Java] 和 资源,以获取更多配置示例。

JSON and XML

对于Content Negotiation目的,能够根据客户端请求的内容类型,交替使用 HTML 模板或其他格式(例如 JSON 或 XML)来渲染模型非常有用。为了支持此操作,Spring WebFlux 提供`HttpMessageWriterView`,你可以使用它来插入任何可用的Codecs从`spring-web`,例如`Jackson2JsonEncoder`、Jackson2SmileEncoder`或`Jaxb2XmlEncoder

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

在大多数场景中,模型包含多个属性。为了确定序列化哪一个,您可以用要用于渲染的模型属性的名称来配置 HttpMessageWriterView。如果模型只包含一个属性,那么就使用该属性。