Spring Web Reactive | 1. Spring WebFlux | 1.9. View Technologies

Web MVC

The use of view technologies in Spring WebFlux is pluggable. Whether you use Thymeleaf, FreeMarker, or another display technology is mostly a matter of configuration changes. This chapter describes view technologies integrated with Spring WebFlux. It assumes that you are already familiar with View Resolution.

1.9.1. Thymeleaf

Web MVC

Thymeleaf is a modern server-side Java template engine that emphasizes natural HTML templates that can be previewed in a browser by double-clicking. This is very helpful for UI templates, such as those handled by designers, without requiring a running server. Thymeleaf provides a broad feature set and is actively developed and maintained. For a complete introduction, see the Thymeleaf project homepage.

The Thymeleaf integration with Spring WebFlux is managed by the Thymeleaf project. Configuration includes several Bean declarations such as SpringResourceTemplateResolver, SpringWebFluxTemplateEngine, and ThymeleafReactiveViewResolver. For details, see Thymeleaf+Spring and the WebFlux integration announcement.

1.9.2. FreeMarker

Web MVC

Apache FreeMarker is a template engine for generating all kinds of text output, from HTML to email. The Spring Framework has basic integration for using FreeMarker templates with Spring WebFlux.

View Configuration

Web MVC

The following example shows how to configure FreeMarker as a view technology.

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")
    }
}

Templates should be stored in the directory specified by FreeMarkerConfigurer shown in the previous example. With the configuration above, if a controller returns the view name welcome, the resolver looks for the classpath:/templates/freemarker/welcome.ftl template.

FreeMarker Configuration

Web MVC

By setting appropriate Bean properties on the FreeMarkerConfigurer Bean, you can pass FreeMarker “settings” and “SharedVariables” directly to the FreeMarker Configuration object managed by Spring. The freemarkerSettings property requires a java.util.Properties object, and the freemarkerVariables property requires a java.util.Map. The following example shows how to use 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()))
    }
}

For details about settings and variables applied to the Configuration object, see the FreeMarker documentation.

Form Handling

Web MVC

Spring provides a tag library used especially in JSP that includes the <spring:bind/> element. This element lets forms mainly display values from the backing object and see validation failures from a Validator in the web or business layer. Spring supports similar functionality with FreeMarker, and also provides additional convenient macros for generating form input elements themselves.

The Bind Macros

Web MVC

The standard set of macros is maintained in the spring-webflux.jar file for FreeMarker, so it is always available in properly configured applications.

Some macros defined in the Spring template library are considered internal or private, but macro definitions do not have that kind of scope, and all macros are visible to calling code and user templates. The following sections focus only on macros that need to be called directly from templates. If you want to view the macro code directly, it is included in the org.springframework.web.reactive.result.view.freemarker package in a file named spring.ftl.

For details about binding support, see Simple Binding in Spring MVC.

Form Macros

For details about Spring’s form macro support for FreeMarker templates, see the following sections in the Spring MVC documentation.

1.9.3. Script Views

Web MVC

The Spring Framework includes built-in integration for using Spring WebFlux with template libraries that can run on a JSR-223 Java script engine. The following table shows template libraries tested with various script engines.

Script library Script engine
Handlebars Nashorn
Mustache Nashorn
React Nashorn
EJS Nashorn
ERB JRuby
String templates Jython
Kotlin script templates Kotlin

The basic rule for integrating another script engine is that it must implement the ScriptEngine and Invocable interfaces.

Requirements

Web MVC

A script engine is required on the classpath. Details depend on the script engine.

  • The Nashorn JavaScript engine is included with Java 8+. It is recommended to use the latest available update.
  • JRuby must be added as a dependency for Ruby support.
  • Jython must be added as a dependency for Python support.
  • To support Kotlin scripts, you must add the org.jetbrains.kotlin:kotlin-script-util dependency and a META-INF/services/javax.script.ScriptEngineFactory file that contains the org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory line. For details, see the example.

A script template library is also required. One way to do this in JavaScript is to use WebJars.

Script Templates

Web MVC

You can declare a ScriptTemplateConfigurer Bean and specify the script engine to use, the script files to load, and the function to call to render templates. The following example uses Mustache templates and the Nashorn JavaScript engine.

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"
    }
}

The render function is called with the following parameters:

  • String template: the contents of the template
  • Map model: the view model
  • RenderingContext renderingContext: a RenderingContext that provides access to the application context, locale, template loader, and URL, since 5.0

Mustache.render() is basically compatible with the signature and can be called directly.

If your template technology requires it, you can provide a script that implements a custom rendering function. For example, Handlebars must compile templates before use and requires a polyfill to emulate browser features that are not available in the server-side script engine. The following example shows how to configure a custom render function.

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
    }
}

If you use a non-thread-safe script engine with template libraries not designed for concurrent execution, such as Handlebars or React running on Nashorn, you must set the sharedEngine property to false. In this case, Java SE 8 update 60 is required because of this bug, but generally it is recommended to use the latest Java SE patch release.

The following fragment in polyfill.js defines only the window object needed for Handlebars to run properly.

var window = {};

This basic render.js implementation compiles the template before using it. A production-ready implementation should also store and reuse cached or precompiled templates. This can be done on the script side as well as by the user as needed, such as for template engine configuration management. The following example shows how to compile a template.

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

For other configuration examples, see the Spring Framework unit tests, Java and resources.

1.9.4. JSON and XML

Web MVC

The purpose of Content Negotiation is to conveniently switch rendering of a model from an HTML template to another format, such as JSON or XML, according to the content type requested by the client. To support this, Spring WebFlux provides HttpMessageWriterView. You can use it to plug in one of the codecs available in spring-web, such as Jackson2JsonEncoder, Jackson2SmileEncoder, or Jaxb2XmlEncoder.

Unlike other view technologies, HttpMessageWriterView does not require a ViewResolver; instead, it is configured as a default view. You can configure one or more of these default views by wrapping different HttpMessageWriter instances or Encoder instances. The matching one is used when it matches the requested content type.

In most cases, the model contains multiple attributes. To decide which one to serialize, you can configure HttpMessageWriterView with the name of the model attribute to use for rendering. If the model contains only one attribute, that attribute is used.