Skip to content

Latest commit

 

History

History
296 lines (223 loc) · 10.8 KB

webflux-view.adoc

File metadata and controls

296 lines (223 loc) · 10.8 KB

View Technologies

The use of view technologies in Spring WebFlux is pluggable, whether you decide to use Thymeleaf, FreeMarker, or other, is primarily a matter of a configuration change. This chapter covers view technologies integrated with Spring WebFlux. We assume you are already familiar with [webflux-viewresolution].

Thymeleaf

Thymeleaf is modern server-side Java template engine that emphasizes natural HTML templates that can be previewed in a browser by double-clicking, which is very helpful for independent work on UI templates, e.g. by designer, without the need for a running server. Thymeleaf offers an extensive set of features and it is actively developed and maintained. For a more complete introduction see the Thymeleaf project home page.

The Thymeleaf integration with Spring WebFlux is managed by the Thymeleaf project. The configuration involves a few bean declarations such as SpringResourceTemplateResolver, SpringWebFluxTemplateEngine, and ThymeleafReactiveViewResolver. For more details see Thymeleaf+Spring and the WebFlux integration announcement.

FreeMarker

Apache FreeMarker is a template engine for generating any kind of text output from HTML to email, and others. The Spring Framework has a built-in integration for using Spring WebFlux with FreeMarker templates.

View config

To configure FreeMarker as a view technology:

@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");
		return configurer;
	}
}

Your templates need to be stored in the directory specified by the FreeMarkerConfigurer shown above. Given the above configuration if your controller returns the view name "welcome" then the resolver will look for the classpath:/templates/freemarker/welcome.ftl template.

FreeMarker config

FreeMarker 'Settings' and 'SharedVariables' can be passed directly to the FreeMarker Configuration object managed by Spring by setting the appropriate bean properties on the FreeMarkerConfigurer bean. The freemarkerSettings property requires a java.util.Properties object and the freemarkerVariables property requires a java.util.Map.

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

See the FreeMarker documentation for details of settings and variables as they apply to the Configuration object.

Script Views

The Spring Framework has a built-in integration for using Spring WebFlux with any templating library that can run on top of the JSR-223 Java scripting engine. Below is a list of templating libraries we’ve tested on different script engines:

Tip

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

Requirements

You need to have the script engine on your classpath:

  • Nashorn JavaScript engine is provided with Java 8+. Using the latest update release available is highly recommended.

  • JRuby should be added as a dependency for Ruby support.

  • Jython should be added as a dependency for Python support.

  • org.jetbrains.kotlin:kotlin-script-util dependency and a META-INF/services/javax.script.ScriptEngineFactory file containing a org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory line should be added for Kotlin script support, see this example for more details.

You need to have the script templating library. One way to do that for Javascript is through WebJars.

Script templates

Declare a ScriptTemplateConfigurer bean in order to specify the script engine to use, the script files to load, what function to call to render templates, and so on. Below is an example with Mustache templates and the Nashorn JavaScript engine:

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

The render function is called with the following parameters:

  • String template: the template content

  • Map model: the view model

  • RenderingContext renderingContext: the {api-spring-framework}/web/servlet/view/script/RenderingContext.html[RenderingContext] that gives access to the application context, the locale, the template loader and the url (since 5.0)

Mustache.render() is natively compatible with this signature, so you can call it directly.

If your templating technology requires some customization, you may provide a script that implements a custom render function. For example, Handlerbars needs to compile templates before using them, and requires a polyfill in order to emulate some browser facilities not available in the server-side script engine.

@Configuration
@EnableWebMvc
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;
	}
}
Note

Setting the sharedEngine property to false is required when using non thread-safe script engines with templating libraries not designed for concurrency, like Handlebars or React running on Nashorn for example. In that case, Java 8u60 or greater is required due to this bug.

polyfill.js only defines the window object needed by 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 reused cached templates / pre-compiled templates. This can be done on the script side, as well as any customization you need (managing template engine configuration for example).

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

Check out the Spring Framework unit tests, java, and resources, for more configuration examples.

JSON, XML

For [webflux-multiple-representations] purposes it is useful to be able to alternate between rendering a model with an HTML template or as other formats such as JSON or XML, depending on the content type requested by the client. To support this Spring WebFlux provides the HttpMessageWriterView that can be used to plug in any of the available [webflux-codecs] from spring-web such as Jackson2JsonEncoder, Jackson2SmileEncoder, or Jaxb2XmlEncoder.

Unlike other view technologies, HttpMessageWriterView does not require a ViewResolver, but instead is configured as a default view. You can configure one more such default views, wrapping different HttpMessageWriter's or Encoder's. The one that matches the requested content type is used at runtime.

In most cases a model will contain multiple attributes. In order to determine which one to serialize, HttpMessageWriterView can be configured with the name of the model attribute to use render, of if the model contains only one attribute, it will be used.