檢視技術
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 內建了使用 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 Framework 內建了與任何可在 JSR-223 Java 指令碼引擎上執行的模板庫一起使用 Spring WebFlux 的整合。下表顯示了我們在不同指令碼引擎上測試過的模板庫
| 指令碼庫 | 指令碼引擎 |
|---|---|
整合任何其他指令碼引擎的基本規則是它必須實現 ScriptEngine 和 Invocable 介面。 |
要求
您需要在類路徑中包含指令碼引擎,具體細節因指令碼引擎而異
-
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 renderingContext: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 上執行的 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);
}
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(伺服器傳送事件)進行流式更新。控制器可以使用 Flux<Fragment> 建立 FragmentsRendering,或者透過 ReactiveAdapterRegistry 將任何其他反應式生產者適應於 Reactive Streams Publisher。也可以直接返回 Flux<Fragment> 而無需 FragmentsRendering 包裝器。
JSON 和 XML
出於 內容協商 目的,根據客戶端請求的內容型別,能夠交替使用 HTML 模板渲染模型或以其他格式(如 JSON 或 XML)渲染模型非常有用。為了支援這樣做,Spring WebFlux 提供了 HttpMessageWriterView,您可以使用它來插入 spring-web 中的任何可用 編解碼器,例如 Jackson2JsonEncoder、Jackson2SmileEncoder 或 Jaxb2XmlEncoder。
與其他檢視技術不同,HttpMessageWriterView 不需要 ViewResolver,而是 配置 為預設檢視。您可以配置一個或多個這樣的預設檢視,包裝不同的 HttpMessageWriter 例項或 Encoder 例項。與請求的內容型別匹配的例項將在執行時使用。
在大多數情況下,模型包含多個屬性。為了確定要序列化哪個屬性,您可以使用用於渲染的模型屬性的名稱來配置 HttpMessageWriterView。如果模型只包含一個屬性,則使用該屬性。