跳到主要内容

视图技术

ChatGPT-4o 中英对照 View Technologies

在 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 框架内置了与 FreeMarker 模板结合使用 Spring WebFlux 的集成。

视图配置

下面的示例显示了如何将 FreeMarker 配置为视图技术:

@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;
}
}
java

您的模板需要存储在 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

@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;
}
}
java

请参阅 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 对 FreeMarker 模板的表单宏支持的详细信息,请查阅 Spring MVC 文档的以下部分。

脚本视图

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

提示

集成任何其他脚本引擎的基本规则是它必须实现 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 引擎:

@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;
}
}
java

render 函数被调用时使用以下参数:

  • String template: 模板内容

  • Map model: 视图模型

  • RenderingContext renderingContext: RenderingContext 提供对应用程序上下文、本地化、模板加载器和 URL 的访问(自 5.0 起)

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

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

@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;
}
}
java
备注

sharedEngine 属性设置为 false 是必要的,当使用不支持线程安全的脚本引擎与不为并发设计的模板库时,例如在 Nashorn 上运行的 Handlebars 或 React。在这种情况下,由于这个 bug,需要 Java SE 8 更新 60,但通常建议使用最新的 Java SE 补丁版本。

polyfill.js 仅定义了 Handlebars 正常运行所需的 window 对象,如以下代码片段所示:

var window = {};
javascript

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

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

查看 Spring Framework 单元测试,Java资源,以获取更多配置示例。

HTML 片段

HTMXHotwire Turbo 强调一种 HTML-over-the-wire 的方法,其中客户端接收服务器更新的内容是 HTML 而不是 JSON。这允许在不需要编写很多甚至任何 JavaScript 的情况下享受 SPA(单页应用)的优势。有关详细概述和更多信息,请访问它们各自的网站。

在 Spring WebFlux 中,视图渲染通常涉及指定一个视图和一个模型。然而,在 HTML-over-the-wire 中,一个常见的功能是发送多个 HTML 片段,浏览器可以使用这些片段来更新页面的不同部分。为此,控制器方法可以返回 Collection<Fragment>。例如:

@GetMapping
List<Fragment> handle() {
return List.of(Fragment.create("posts"), Fragment.create("comments"));
}
java

同样也可以通过返回专用类型 FragmentsRendering 来实现:

@GetMapping
FragmentsRendering handle() {
return FragmentsRendering.with("posts").fragment("comments").build();
}
java

每个片段可以有一个独立的模型,并且该模型从请求的共享模型继承属性。

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 来指定用于渲染的模型属性名称。如果模型仅包含一个属性,则使用该属性。