Micronaut Views

Provides integration between Micronaut and server-side views technologies

Version: 1.2.0.RC1

1 Introduction

This project integrates Micronaut and Server Side View Rendering.

2 Server Side View Rendering

Although Micronaut is primarily designed around message encoding / decoding there are occasions where it is convenient to render a view on the server side.

The views module provides support for view rendering on the server side and does so by rendering views on the I/O thread pool in order to avoid blocking the Netty event loop.

To use the view rendering features described in this section, add the following dependency on your classpath.

compile 'io.micronaut:micronaut-views'
<dependency>
    <groupId>io.micronaut</groupId>
    <artifactId>micronaut-views</artifactId>
</dependency>

Views and templates can then be placed in the src/main/resources/views directory of your project.

If you wish to use a different folder instead of views, set the property micronaut.views.folder.

Your controller’s method can render the response with a template with the the View annotation.

The following is an example of a controller which renders a template by passing a model as a java.util.Map via the returned response object.

src/main/java/myapp/ViewsController.java
@Controller("/views")
class ViewsController {

    @View("home")
    @Get("/")
    public HttpResponse index() {
        return HttpResponse.ok(CollectionUtils.mapOf("loggedIn", true, "username", "sdelamo"))
    }

}
1 Use @View annotation to indicate the view name which should be used to render a view for the route.

In addition, you can return any POJO object and the properties of the POJO will be exposed to the view for rendering:

src/main/java/myapp/ViewsController.java
@Controller("/views")
class ViewsController {

    @View("home")
    @Get("/pogo")
    public HttpResponse<Person> pogo() {
        return HttpResponse.ok(new Person("sdelamo", true))
    }

}
1 Use @View annotation to indicate the view name which should be used to render the POJO responded by the controller.

You can also return a ModelAndView and skip specifying the View annotation.

src/main/java/myapp/ViewsController.java
@Controller("/views")
class ViewsController {

    @Get("/modelAndView")
    ModelAndView modelAndView() {
        return new ModelAndView("home",
                new Person(loggedIn: true, username: 'sdelamo'))
    }

You can also provide implementations of ViewsModelDecorator to decorate the model with extra properties.

The following sections show different template engines integrations.

To create your own implementation create a class which implements ViewsRenderer and annotate it with @Produces to the media types the view rendering supports producing.

2.1 Thymeleaf

Micronaut includes ThymeleafViewsRenderer which uses the Thymeleaf Java template engine.

In addition to the Views dependency, add the following dependency on your classpath.

runtime 'org.thymeleaf:thymeleaf:3.0.11.RELEASE'
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
    <scope>runtime</scope>
</dependency>

Thymeleaf integration instantiates a ClassLoaderTemplateResolver.

The properties used can be customized by overriding the values of:

🔗
Table 1. Configuration Properties for ThymeleafViewsRendererConfigurationProperties
Property Type Description

micronaut.views.thymeleaf.enabled

boolean

Sets whether thymeleaf rendering is enabled. Default value (true).

micronaut.views.thymeleaf.character-encoding

java.lang.String

Sets the character encoding to use. Default value ("UTF-8").

micronaut.views.thymeleaf.template-mode

org.thymeleaf.templatemode.TemplateMode

Sets the template mode.

micronaut.views.thymeleaf.suffix

java.lang.String

Sets the suffix to use.

micronaut.views.thymeleaf.force-suffix

boolean

Sets whether to force the suffix. Default value (false).

micronaut.views.thymeleaf.force-template-mode

boolean

Sets whether to force template mode. Default value (false).

micronaut.views.thymeleaf.cacheable

boolean

Sets whether templates are cacheable.

micronaut.views.thymeleaf.cache-ttlms

java.lang.Long

Sets the cache TTL in millis.

micronaut.views.thymeleaf.check-existence

boolean

Sets whether templates should be checked for existence.

micronaut.views.thymeleaf.cache-ttl

java.time.Duration

Sets the cache TTL as a duration.

The example shown in the Views section, could be rendered with the following Thymeleaf template:

src/main/resources/views/home.html
<!DOCTYPE html>
<html lang="en" th:replace="~{layoutFile :: layout(~{::title}, ~{::section})}" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Home</title>
</head>
<body>
<section>
    <h1 th:if="${loggedIn}">username: <span th:text="${username}"></span></h1>
    <h1 th:unless="${loggedIn}">You are not logged in</h1>
</section>
</body>
</html>

and layout:

src/main/resources/views/layoutFile.html
<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
    <title th:replace="${title}">Layout Title</title>
</head>
<body>
<h1>Layout H1</h1>
<div th:replace="${content}">
    <p>Layout content</p>
</div>
<footer>
    Layout footer
</footer>
</body>
</html>

2.2 Handlebars.java

Micronaut includes HandlebarsViewsRenderer which uses the Handlebars.java project.

In addition to the Views dependency, add the following dependency on your classpath. For example, in build.gradle

runtime 'com.github.jknack:handlebars:4.1.0'
<dependency>
    <groupId>com.github.jknack</groupId>
    <artifactId>handlebars</artifactId>
    <version>4.1.0</version>
    <scope>runtime</scope>
</dependency>

The example shown in the Views section, could be rendered with the following Handlebars template:

src/main/resources/views/home.hbs
<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    {{#if loggedIn}}
    <h1>username: <span>{{username}}</span></h1>
    {{else}}
    <h1>You are not logged in</h1>
    {{/if}}
</body>
</html>

2.3 Apache Velocity

Micronaut includes VelocityViewsRenderer which uses the Apache Velocity Java-based template engine.

In addition to the Views dependency, add the following dependency on your classpath.

runtime 'org.apache.velocity:velocity-engine-core:2.0'
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.0</version>
    <scope>runtime</scope>
</dependency>

The example shown in the Views section, could be rendered with the following Velocity template:

src/main/resources/views/home.vm
<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    #if( $loggedIn )
    <h1>username: <span>$username</span></h1>
    #else
    <h1>You are not logged in</h1>
    #end
</body>
</html>

2.4 Apache Freemarker

Micronaut includes FreemarkerViewsRenderer which uses the Apache Freemarker Java-based template engine.

In addition to the Views dependency, add the following dependency on your classpath.

runtime 'org.freemarker:freemarker:2.3.28'
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.28</version>
    <scope>runtime</scope>
</dependency>

The example shown in the Views section, could be rendered with the following Freemarker template:

src/main/resources/views/home.ftl
<#--

    Copyright 2017-2019 original authors

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <#if loggedIn??>
    <h1>username: <span>${username}</span></h1>
    <#else>
    <h1>You are not logged in</h1>
    </#if>
</body>
</html>

Freemarker integration instantiates a freemarker Configuration.

All configurable properties are extracted from Configuration and Configurable, and properties names are reused in the Micronaut configuration.

If a value is not declared and is null, the default configuration from Freemarker is used. The expected format of each value is the same from Freemarker, and no conversion or validation is done by Micronaut. You can find in Freemarker documentation how to configure each one.

2.5 CSP Header Support

Micronaut supports CSP (Content Security Policy Level 2) out of the box. By default, CSP is disabled. To enable CSP, modify your configuration. For example:

application.yml
micronaut:
    views:
        csp:
            enabled: true

See the following table for all configuration options:

🔗
Table 1. Configuration Properties for CspConfiguration
Property Type Description

micronaut.views.csp.enabled

boolean

Sets whether CSP is enabled. Default false.

micronaut.views.csp.policy-directives

java.lang.String

Sets the policy directives.

micronaut.views.csp.report-only

boolean

If true, the Content-Security-Policy-Report-Only header will be sent instead of Content-Security-Policy. Default false.

micronaut.views.csp.filter-path

java.lang.String

Sets the path the CSP filter should apply to. Default value "/**".

2.6 Security Integration

The views project has integration with the Micronaut security project. SecurityViewModelProcessor is enabled by default and injects the current username in the view. The following properties allow you to customize the injection:

🔗
Table 1. Configuration Properties for SecurityViewModelProcessorConfigurationProperties
Property Type Description

micronaut.security.views-model-decorator.enabled

boolean

Enable {@link SecurityViewModelProcessor}. Default value (true).

micronaut.security.views-model-decorator.security-key

java.lang.String

Model key name. Default value ("security").

micronaut.security.views-model-decorator.principal-name-key

java.lang.String

Nested security map key for the user’s name property. Default value ("name").

micronaut.security.views-model-decorator.attributes-key

java.lang.String

Nested security map key for the user’s attributes property. Default value ("attributes").

In a controller, you can return a model without specifying in the model the authenticated user:

src/main/java/myapp/BooksController.java
@Controller("/")
public class BooksController {

    @Secured(SecurityRule.IS_AUTHENTICATED)
    @View("securitydecorator")
    @Get
    public Map<String, Object> index() {
        Map<String, Object> model = new HashMap<>();
        model.put("books", Arrays.asList("Developing Microservices"));
        return model;
    }
}

and still access the authenticated user in the view (for example a velocity template):

src/main/resources/views/books.vm
<!DOCTYPE html>
<html>
<head>
    <title>User Books</title>
</head>
<body>
    #if( $security )
      <h1>User: ${security['name']} email: ${security['attributes']['email']}</h1>
    #end

    #foreach($book in $books)
      $book
    #end

    #if( $securitycustom )
    <h1>Custom: ${securitycustom['name']}</span></h1>
    #end

</body>
</html>

You can access information about the current user with the security map.