ビューテクノロジー

Spring WebFlux のビューのレンダリングはプラグ可能です。Thymeleaf、FreeMarker、その他のビューテクノロジを使用するかどうかは、主に構成の変更によって決まります。この章では、Spring WebFlux に統合されたビューテクノロジについて説明します。

ビューのレンダリングの詳細については、ビューリゾルバーを参照してください。

Spring WebFlux アプリケーションのビューは、アプリケーションの内部信頼境界内に存在します。ビューはアプリケーションコンテキスト内の Bean にアクセスできるため、セキュリティに影響する可能性があるため、テンプレートが外部ソースによって編集可能なアプリケーションでは Spring WebFlux テンプレートサポートを使用しないことをお勧めします。

Thymeleaf

Thymeleaf は、ダブルクリックでブラウザーでプレビューできる自然な HTML テンプレートを強調する最新のサーバー側 Java テンプレートエンジンです。これは、UI テンプレート(デザイナーなど)での独立した作業に必要なく、非常に役立ちます。実行中のサーバー。Thymeleaf は広範な機能セットを提供し、積極的に開発および保守されています。より完全な導入については、Thymeleaf (英語) プロジェクトのホームページを参照してください。

Thymeleaf と Spring の統合 WebFlux は、Thymeleaf プロジェクトによって管理されます。構成には、SpringResourceTemplateResolverSpringWebFluxTemplateEngineThymeleafReactiveViewResolver などのいくつかの Bean 宣言が含まれます。詳細については、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 の「設定」と "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 は、特に <spring:bind/> 要素を含む JSP で使用するタグライブラリを提供します。この要素により、フォームは主にフォームバッキングオブジェクトの値を表示し、Web またはビジネス層の Validator からの検証の失敗の結果を表示できます。Spring は、FreeMarker と同じ機能をサポートしていますが、フォーム入力要素自体を生成するための追加の便利なマクロがあります。

バインドマクロ

マクロの標準セットは、FreeMarker の spring-webflux.jar ファイル内で維持されるため、適切に構成されたアプリケーションで常に使用可能です。

Spring テンプレートライブラリで定義されているマクロの一部は内部(プライベート)と見なされますが、マクロ定義にはそのようなスコープは存在せず、呼び出し元のコードとユーザーテンプレートにすべてのマクロが表示されます。次のセクションでは、テンプレート内から直接呼び出す必要があるマクロのみに焦点を当てます。マクロコードを直接表示する場合、ファイルは spring.ftl と呼ばれ、org.springframework.web.reactive.result.view.freemarker パッケージに含まれています。

バインディングサポートの詳細については、Spring MVC の簡単なバインドを参照してください。

フォームマクロ

FreeMarker テンプレートに対する Spring のフォームマクロサポートの詳細については、Spring MVC ドキュメントの次のセクションを参照してください。

スクリプトビュー

Spring Framework には、JSR-223 (英語) Java スクリプトエンジン上で実行できるテンプレートライブラリで Spring WebFlux を使用するための組み込みの統合があります。次の表は、さまざまなスクリプトエンジンでテストしたテンプレートライブラリを示しています。

スクリプトライブラリ スクリプトエンジン

Handlebars (英語)

Nashorn (英語)

Mustache (英語)

Nashorn (英語)

React (英語)

Nashorn (英語)

EJS (英語)

Nashorn (英語)

ERB (英語)

JRuby (英語)

文字列テンプレート (英語)

Jython (英語)

Kotlin スクリプトテンプレート [GitHub] (英語)

Kotlin (英語)

他のスクリプトエンジンを統合するための基本的なルールは、ScriptEngine および Invocable インターフェースを実装する必要があるということです。

要件

クラスパスにスクリプトエンジンが必要です。詳細はスクリプトエンジンによって異なります。

  • Nashorn (英語) JavaScript エンジンには Java 8+ が付属しています。利用可能な最新の更新リリースを使用することを強くお勧めします。

  • JRuby (英語) は、Ruby サポートの依存関係として追加する必要があります。

  • Jython (英語) は、Python サポートの依存関係として追加する必要があります。

  • Kotlin スクリプトをサポートするには、org.jetbrains.kotlin:kotlin-script-util 依存関係と org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory 行を含む META-INF/services/javax.script.ScriptEngineFactory ファイルを追加する必要があります。詳細については、この例 [GitHub] (英語) を参照してください。

スクリプトテンプレートライブラリが必要です。JavaScript でこれを行う 1 つの方法は、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: アプリケーションコンテキスト、ロケール、テンプレートローダー、URL へのアクセスを提供する RenderingContext (Javadoc) (5.0 以降)

Mustache.render() はこの署名とネイティブに互換性があるため、直接呼び出すことができます。

テンプレートテクノロジでカスタマイズが必要な場合は、カスタムレンダリング機能を実装するスクリプトを提供できます。例: Handlerbars (英語) は、使用する前にテンプレートをコンパイルする必要があり、サーバーサイドスクリプトエンジンでは利用できないブラウザー機能をエミュレートするために、ポリフィル [Mozilla] が必要です。次の例は、カスタムレンダリング関数を設定する方法を示しています。

  • 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 (参考: JavaScript フロントエンドフレームワークの比較 [Qiita] ) など、同時実行用に設計されていないテンプレートライブラリで非スレッドセーフスクリプトエンジンを使用する場合は、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 [GitHub] (英語) リソース [GitHub] (英語) を参照してください。

HTML フラグメント

HTMX (英語) ホットワイヤーターボ (英語) は、クライアントがサーバーの更新を JSON ではなく HTML で受信する HTML over the wire アプローチを重視しています。これにより、JavaScript をほとんどまたはまったく記述しなくても、SPA (シングルページアプリ) の利点を活用できます。概要と詳細については、それぞれの Web サイトにアクセスしてください。

Spring WebFlux では、ビューのレンダリングには通常、1 つのビューと 1 つのモデルを指定することが含まれます。ただし、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.with("posts").fragment("comments").build();
}
@GetMapping
fun handle(): FragmentsRendering {
	return FragmentsRendering.with("posts").fragment("comments").build()
}

各フラグメントは独立したモデルを持つことができ、そのモデルはリクエストの共有モデルから属性を継承します。

HTMX と Hotwire Turbo は、SSE (サーバー送信イベント) 経由のストリーミング更新をサポートします。コントローラーは、Flux<Fragment> を使用して FragmentsRendering を作成したり、ReactiveAdapterRegistry を介して Reactive Streams Publisher に適応可能な他のリアクティブプロデューサーを使用して ReactiveAdapterRegistry を作成したりできます。また、FragmentsRendering ラッパーを使用せずに Flux<Fragment> を直接返すこともできます。

JSON と XML

コンテンツネゴシエーションの目的では、クライアントからリクエストされたコンテンツ型に応じて、HTML テンプレートを使用したモデルのレンダリングと他の形式(JSON や XML など)としてのレンダリングを切り替えることができると便利です。そうすることをサポートするために、Spring WebFlux は HttpMessageWriterView を提供します。これを使用して、Jackson2JsonEncoderJackson2SmileEncoderJaxb2XmlEncoder などの spring-web から利用可能なコーデックのいずれかをプラグインできます。

他のビューテクノロジとは異なり、HttpMessageWriterView は ViewResolver を必要としませんが、代わりにデフォルトのビューとして構成されます。さまざまな HttpMessageWriter インスタンスまたは Encoder インスタンスをラップして、このようなデフォルトビューを 1 つ以上構成できます。リクエストされたコンテンツ型に一致するものが実行時に使用されます。

ほとんどの場合、モデルには複数の属性が含まれます。どれを直列化するかを決定するために、レンダリングに使用するモデル属性の名前で HttpMessageWriterView を構成できます。モデルに含まれる属性が 1 つだけの場合、その属性が使用されます。