Skip to content

MVC framework for Bootique and Mustache Template Engine integration

License

Notifications You must be signed in to change notification settings

bootique/bootique-mvc

Repository files navigation

build test deploy Maven Central

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.

Usage

Prerequisites

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 HTML page

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".

Template Resolving

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.

Template Caching

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.