Spring Web Reactive | 1. Spring WebFlux | 1.9. View Technologies
Spring WebFluxにおけるビュー技術の利用はプラグ可能である。Thymeleaf、FreeMarker、その他の表示技術のどれを使うかは、主に構成変更の問題である。この章では、Spring WebFluxと統合されたビュー技術について説明する。すでにView Resolutionについて理解していることを前提としている。
1.9.1. Thymeleaf
Thymeleafは、ダブルクリックでブラウザからプレビューできる自然なHTMLテンプレートを重視する、最新のサーバーサイドJavaテンプレートエンジンである。これは、実行中のサーバーを必要とせず、UIテンプレート(デザイナーなど)で別作業を行う際に非常に役立つ。Thymeleafは幅広い機能セットを提供し、活発に開発および保守されている。完全な紹介については、Thymeleafプロジェクトのホームページを参照してほしい。
ThymeleafとSpring WebFluxの統合はThymeleafプロジェクトによって管理されている。構成には、SpringResourceTemplateResolver、SpringWebFluxTemplateEngine、ThymeleafReactiveViewResolverなど、いくつかのBean宣言が含まれる。詳細はThymeleaf+SpringとWebFlux統合の発表を参照してほしい。
1.9.2. FreeMarker
Apache FreeMarkerは、HTMLからメールまで、あらゆる種類のテキスト出力を生成するためのテンプレートエンジンである。Spring Frameworkには、Spring WebFluxでFreeMarkerテンプレートを使用するための基本的な統合機能がある。
ビュー設定(View Configuration)
次の例は、FreeMarkerをビュー技術として設定する方法を示している。
Java
@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;
}
}
Kotlin
@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」を、Springによって管理されるFreeMarker Configurationオブジェクトへ直接渡すことができる。freemarkerSettings属性にはjava.util.Propertiesオブジェクトが必要であり、freemarkerVariables属性にはjava.util.Mapが必要である。次の例はFreeMarkerConfigurerの使用方法を示している。
Java
@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;
}
}
Kotlin
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {
// ...
@Bean
fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply {
setTemplateLoaderPath("classpath:/templates")
setFreemarkerVariables(mapOf("xml_escape" to XmlEscape()))
}
}
Configurationオブジェクトに適用される設定および変数の詳細については、FreeMarkerドキュメントを参照してほしい。
フォーム処理(Form Handling)
Springは、特に<spring:bind/>要素を含むJSPで使用するタグライブラリを提供する。この要素により、フォームは主にフォームのバッキングオブジェクトの値を表示し、Web層またはビジネス層のValidatorによる検証失敗の結果を確認できる。SpringはFreeMarkerでも同様の機能をサポートしており、フォーム入力要素自体を生成するための追加の便利なマクロも提供している。
バインディングマクロ(The Bind Macros)
標準マクロセットはFreeMarker用のspring-webflux.jarファイルに保持されているため、適切に構成されたアプリケーションでは常に使用できる。
Springテンプレートライブラリに定義されているマクロの一部は内部(非公開)とみなされるが、マクロ定義にはそのようなスコープは存在せず、呼び出し元コードとユーザーテンプレートからすべてのマクロが見える。次のセクションでは、テンプレートから直接呼び出す必要があるマクロだけに焦点を当てる。マクロコードを直接確認する場合は、org.springframework.web.reactive.result.view.freemarkerパッケージに含まれるspring.ftlというファイルにある。
バインディングサポートの詳細については、Spring MVCのSimple Bindingを参照してほしい。
フォームマクロ(Form Macros)
FreeMarkerテンプレートに対するSpringのフォームマクロサポートの詳細については、Spring MVCドキュメントの次のセクションを参照してほしい。
1.9.3. スクリプトビュー
Spring Frameworkには、JSR-223 Javaスクリプトエンジンで実行できるテンプレートライブラリをSpring WebFluxで使用するための組み込み統合が含まれている。次の表は、さまざまなスクリプトエンジンでテストされたテンプレートライブラリを示している。
| スクリプトライブラリ | スクリプトエンジン |
|---|---|
| Handlebars | Nashorn |
| Mustache | Nashorn |
| React | Nashorn |
| EJS | Nashorn |
| ERB | JRuby |
| String templates | Jython |
| Kotlinスクリプトテンプレート | 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ファイルを追加する必要がある。詳細は例を参照してほしい。
スクリプトテンプレートライブラリも必要である。JavaScriptでこれを行う1つの方法はWebJarsを使用することである。
スクリプトテンプレート(Script Templates)
ScriptTemplateConfigurer Beanを宣言し、使用するスクリプトエンジン、ロードするスクリプトファイル、テンプレートをレンダリングするために呼び出す関数などを指定できる。次の例ではMustacheテンプレートとNashorn JavaScriptエンジンを使用している。
Java
@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;
}
}
Kotlin
@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(5.0以降)
Mustache.render()はシグネチャと基本的に互換性があるため、直接呼び出すことができる。
テンプレート技術で必要な場合、カスタムレンダリング関数を実装するスクリプトを提供できる。たとえば、Handlebarsは使用前にテンプレートをコンパイルする必要があり、サーバーサイドスクリプトエンジンでは利用できないブラウザ機能をエミュレートするためにpolyfillが必要である。次の例は、カスタムレンダリング関数を設定する方法を示している。
Java
@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;
}
}
Kotlin
@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、resourcesを参照してほしい。
1.9.4. JSONとXML
Content Negotiationの目的は、クライアントから要求されたコンテンツ型に応じて、HTMLテンプレートを使ったモデルのレンダリングと、JSONやXMLなど別形式でのレンダリングを便利に切り替えられるようにすることである。これをサポートするために、Spring WebFluxはHttpMessageWriterViewを提供する。これを使って、Jackson2JsonEncoder、Jackson2SmileEncoder、Jaxb2XmlEncoderなど、spring-webで利用可能なコーデックの1つをプラグインできる。
他のビュー技術とは異なり、HttpMessageWriterViewはViewResolverを必要とせず、代わりにデフォルトビューとして設定される。異なるHttpMessageWriterインスタンスまたはEncoderインスタンスをラップし、これらのデフォルトビューを1つ以上構成できる。リクエストされたコンテンツ型に一致するものが実行時に使用される。
ほとんどの場合、モデルには複数の属性が含まれる。どれをシリアライズするかを決定するために、レンダリングで使用するモデル属性名でHttpMessageWriterViewを構成できる。モデルに含まれる属性が1つだけの場合、その属性が使用される。