视图技术

Spring WebFlux 中的视图渲染是可插拔的。您决定使用 Thymeleaf、FreeMarker 还是其他视图技术主要取决于配置更改。本章涵盖了与 Spring WebFlux 集成的视图技术。

有关视图渲染的更多上下文,请参阅 视图解析

Spring WebFlux 应用程序的视图位于应用程序的内部信任边界内。视图可以访问应用程序上下文中的 Bean,因此,我们不建议在模板可由外部源编辑的应用程序中使用 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 'Settings' 和 'SharedVariables' 直接传递给 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 中的相同功能,并提供额外的便捷宏来生成表单输入元素本身。

绑定宏

Spring-webflux.jar 文件中为 FreeMarker 维护了一组标准宏,因此它们始终可用于适当配置的应用程序。

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

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

表单宏

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

脚本视图

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

脚本库 脚本引擎

Handlebars

Nashorn

Mustache

Nashorn

React

Nashorn

EJS

Nashorn

ERB

JRuby

字符串模板

Jython

Kotlin 脚本模板

Kotlin

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

要求

您需要在类路径中包含脚本引擎,具体细节因脚本引擎而异

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

  • 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 update 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资源,以获取更多配置示例。

HTML 片段

HTMXHotwire 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(服务器发送事件)进行流式更新。控制器可以使用 Flux<Fragment> 创建 FragmentsRendering,或者通过 ReactiveAdapterRegistry 将任何其他反应式生产者适应于 Reactive Streams Publisher。也可以直接返回 Flux<Fragment> 而无需 FragmentsRendering 包装器。

JSON 和 XML

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

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

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

© . This site is unofficial and not affiliated with VMware.