3 changed files with 331 additions and 38 deletions
@ -0,0 +1,288 @@
@@ -0,0 +1,288 @@
|
||||
[[webflux-view]] |
||||
= View Technologies |
||||
[.small]#<<web.adoc#mvc-view,Same in Spring MVC>># |
||||
|
||||
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>>. |
||||
|
||||
|
||||
|
||||
|
||||
[[webflux-view-thymeleaf]] |
||||
== Thymeleaf |
||||
[.small]#<<web.adoc#mvc-view-thymeleaf,Same in Spring MVC>># |
||||
|
||||
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 |
||||
http://www.thymeleaf.org/[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 |
||||
http://www.thymeleaf.org/documentation.html[Thymeleaf+Spring] and the WebFlux integration |
||||
http://forum.thymeleaf.org/Thymeleaf-3-0-8-JUST-PUBLISHED-td4030687.html[announcement]. |
||||
|
||||
|
||||
|
||||
|
||||
[[webflux-view-freemarker]] |
||||
== FreeMarker |
||||
[.small]#<<web.adoc#mvc-view-freemarker,Same in Spring MVC>># |
||||
|
||||
http://www.freemarker.org[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. |
||||
|
||||
|
||||
|
||||
[[webflux-view-freemarker-contextconfig]] |
||||
=== View config |
||||
[.small]#<<web.adoc#mvc-view-freemarker-contextconfig,Same in Spring MVC>># |
||||
|
||||
To configure FreeMarker as a view technology: |
||||
|
||||
[source,java,indent=0] |
||||
[subs="verbatim,quotes"] |
||||
---- |
||||
@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. |
||||
|
||||
|
||||
|
||||
[[webflux-views-freemarker]] |
||||
=== FreeMarker config |
||||
[.small]#<<web.adoc#mvc-views-freemarker,Same in Spring MVC>># |
||||
|
||||
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`. |
||||
|
||||
[source,java,indent=0] |
||||
[subs="verbatim,quotes"] |
||||
---- |
||||
@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. |
||||
|
||||
|
||||
|
||||
|
||||
[[webflux-view-script]] |
||||
== Script Views |
||||
[.small]#<<web.adoc#mvc-view-script,Same in Spring MVC>># |
||||
|
||||
The Spring Framework has a built-in integration for using Spring WebFlux with any |
||||
templating library that can run on top of the |
||||
https://www.jcp.org/en/jsr/detail?id=223[JSR-223] Java scripting engine. Below is a list |
||||
of templating libraries we've tested on different script engines: |
||||
|
||||
[horizontal] |
||||
http://handlebarsjs.com/[Handlebars] :: http://openjdk.java.net/projects/nashorn/[Nashorn] |
||||
https://mustache.github.io/[Mustache] :: http://openjdk.java.net/projects/nashorn/[Nashorn] |
||||
http://facebook.github.io/react/[React] :: http://openjdk.java.net/projects/nashorn/[Nashorn] |
||||
http://www.embeddedjs.com/[EJS] :: http://openjdk.java.net/projects/nashorn/[Nashorn] |
||||
http://www.stuartellis.eu/articles/erb/[ERB] :: http://jruby.org[JRuby] |
||||
https://docs.python.org/2/library/string.html#template-strings[String templates] :: http://www.jython.org/[Jython] |
||||
|
||||
[TIP] |
||||
==== |
||||
The basic rule for integrating any other script engine is that it must implement the |
||||
`ScriptEngine` and `Invocable` interfaces. |
||||
==== |
||||
|
||||
|
||||
|
||||
[[webflux-view-script-dependencies]] |
||||
=== Requirements |
||||
[.small]#<<web.adoc#mvc-view-script-dependencies,Same in Spring MVC>># |
||||
|
||||
You need to have the script engine on your classpath: |
||||
|
||||
* http://openjdk.java.net/projects/nashorn/[Nashorn] JavaScript engine is provided with |
||||
Java 8+. Using the latest update release available is highly recommended. |
||||
* http://jruby.org[JRuby] should be added as a dependency for Ruby support. |
||||
* http://www.jython.org[Jython] should be added as a dependency for Python support. |
||||
|
||||
You need to have the script templating library. One way to do that for Javascript is |
||||
through http://www.webjars.org/[WebJars]. |
||||
|
||||
|
||||
|
||||
[[webflux-view-script-integrate]] |
||||
=== Script templates |
||||
[.small]#<<web.adoc#mvc-view-script-integrate,Same in Spring MVC>># |
||||
|
||||
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: |
||||
|
||||
[source,java,indent=0] |
||||
[subs="verbatim,quotes"] |
||||
---- |
||||
@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 |
||||
* `String url`: the template url (since 4.2.2) |
||||
|
||||
`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, http://handlebarsjs.com[Handlerbars] |
||||
needs to compile templates before using them, and requires a |
||||
http://en.wikipedia.org/wiki/Polyfill[polyfill] in order to emulate some |
||||
browser facilities not available in the server-side script engine. |
||||
|
||||
[source,java,indent=0] |
||||
[subs="verbatim,quotes"] |
||||
---- |
||||
@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 https://bugs.openjdk.java.net/browse/JDK-8076099[this bug]. |
||||
==== |
||||
|
||||
`polyfill.js` only defines the `window` object needed by Handlebars to run properly: |
||||
|
||||
[source,javascript,indent=0] |
||||
[subs="verbatim,quotes"] |
||||
---- |
||||
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). |
||||
|
||||
[source,javascript,indent=0] |
||||
[subs="verbatim,quotes"] |
||||
---- |
||||
function render(template, model) { |
||||
var compiledTemplate = Handlebars.compile(template); |
||||
return compiledTemplate(model); |
||||
} |
||||
---- |
||||
|
||||
Check out the Spring Framework unit tests, |
||||
https://github.com/spring-projects/spring-framework/tree/master/spring-webflux/src/test/java/org/springframework/web/reactive/result/view/script[java], and |
||||
https://github.com/spring-projects/spring-framework/tree/master/spring-webflux/src/test/resources/org/springframework/web/reactive/result/view/script[resources], |
||||
for more configuration examples. |
||||
|
||||
|
||||
|
||||
[[webflux-view-httpmessagewriter]] |
||||
== JSON, XML |
||||
[.small]#<<web.adoc#mvc-view-jackson,Same in Spring MVC>># |
||||
|
||||
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 <<webflux-config-view-resolvers,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. |
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in new issue