compile 'io.micronaut:micronaut-views'
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.
<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.
@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:
@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.
@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:
Property | Type | Description |
---|---|---|
|
boolean |
Sets whether thymeleaf rendering is enabled. Default value (true). |
|
java.lang.String |
Sets the character encoding to use. Default value ("UTF-8"). |
|
org.thymeleaf.templatemode.TemplateMode |
Sets the template mode. |
|
java.lang.String |
Sets the suffix to use. |
|
boolean |
Sets whether to force the suffix. Default value (false). |
|
boolean |
Sets whether to force template mode. Default value (false). |
|
boolean |
Sets whether templates are cacheable. |
|
java.lang.Long |
Sets the cache TTL in millis. |
|
boolean |
Sets whether templates should be checked for existence. |
|
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:
<!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:
<!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:
<!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:
<!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:
<#--
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:
micronaut:
views:
csp:
enabled: true
See the following table for all configuration options:
Property | Type | Description |
---|---|---|
|
boolean |
Sets whether CSP is enabled. Default false. |
|
java.lang.String |
Sets the policy directives. |
|
boolean |
If true, the Content-Security-Policy-Report-Only header will be sent instead of Content-Security-Policy. Default false. |
|
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:
Property | Type | Description |
---|---|---|
|
boolean |
Enable {@link SecurityViewModelProcessor}. Default value (true). |
|
java.lang.String |
Model key name. Default value ("security"). |
|
java.lang.String |
Nested security map key for the user’s name property. Default value ("name"). |
|
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:
@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):
<!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.