A basic MVC web framework for Bootique for processing requests and responding with template-generated views. Implemented on top of JAX-RS, specifically bootique-jersey). bootique-mvc can work with multiple template engines, providing integration with Mustache and FreeMarker out of the box.
This framework is suitable for simple HTML UIs, with minimal server-side rendering (e.g. when most of the UI work is done on the client with JavaScript).
Code examples: bootique-mvc-demo.
Include bootique-bom
:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.bootique.bom</groupId>
<artifactId>bootique-bom</artifactId>
<version>3.0-M4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Include the flavor of bootique-mvc you are planning to use, e.g. Jakarta / Mustache:
<dependency>
<groupId>io.bootique.mvc</groupId>
<artifactId>bootique-mvc-jakarta-mustache</artifactId>
</dependency>
Create a "view" class extending AbstractView
. It performs two functions:
- Defines the location of the page template. Pay attention to the view Java package. Usually template file will be located under the path matching the view package (see the next section on template resolving mechanism).
- Serves as a "root context" during template rendering, providing values for the template variables. So the view is also a holder of the page "model".
package org.example.view;
public class SomePageView extends AbstractView {
private final String firstName;
private final String lastName;
public SomePageView(String firstName, String lastName) {
super("some-page.mustache");
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
Configure the app to resolve templates relative to a folder on a classpath :
mvc:
templateBase: "classpath:templates"
Now let's create a Mustache template in the project resources folder under
templates/org/example/view/some-page.mustache
.
<html>
<body>
<h1>Hi, {{firstName}} {{lastName}}!</h1>
</body>
</html>
Finally, create a "controller" class, which is a collection of JAX-RS endpoints that return view instances in response to user requests. So a single controller can serve multiple "pages" (e.g. those that share a common URL path prefix).
@Path("/")
@Produces(MediaType.TEXT_HTML)
public static class Pages {
@GET
@Path("some-page")
public SomePageView somePage(
@QueryParam("fn") String firstName,
@QueryParam("ln") String lastName) {
return new SomePageView(firstName, lastName);
}
}
Now when you hit /some-page?fn=Joe&ln=Smith
, you'd get a page that says "hi".
In the example above we set the template base to be classpath:templates
. But it can also be set to a filesystem
directory or a URL on a public web server. The only requirement is that a single base is shared by all templates.
Assuming the base is classpath:templates
, here are some simple rules for path resolution:
- Template path with no leading forward slash will be prepended with a path corresponding to the view package:
some-page.mustache
->classpath:templates/org/example/view/some-page.mustache
. - Template path starting with a forward slash is resolved directly against
templateBase
:/some-page.mustache
->classpath:templates/some-page.mustache
- Template path can reference parent directories via
../
:../some-page.mustache
->classpath:templates/org/example/some-page.mustache
. - If a parent directory is outside the
templateBase
, an exception is thrown:../../../../some-page.mustache
-> throws - Templates can include other templates (such includes are called "partials" in Mustache). The rules for resolving includes are the same as for the root templates.
By default bootique-mvc
would reload a template on every call. This is great in development mode, but is
going to result in poor performance in production. To configure template caching, you'll need to set an extra
property in config. E.g.:
mvc:
templateTtl: 1min
TODO: as of Bootique 3.0.M2, this only works for Mustache, and not Freemarker until this task is complete.