Offering the same data as both JSON and HTML

To offer the same data as JSON or HTML we could use different strategies. The two simplest ones are:

  • using different endpoints for JSON and HTML
  • use the Accept header to determine what format of data to return

The first strategy is very simple to implement: you just have to create different routes. For example, we could offer the JSON data in response to the endpoints /service/posts and /service/posts/:id, while offering HTML data in response to the endpoints /posts and /posts/:id. This is because we typically want shorter URLs for the content intended for human beings. In this post we will focus on the Accept-header strategy though, which requires a bit of work.

Using the Accept header to decide to return JSON or HTML

An HTTP request reaching our service brings a lot of information, including the URL (of which we can parse specific parts to derive parameters), query parameters, a body and headers.
An interesting header is Accept. It can be used to specify the format that the caller is able to process or the formats it prefers. A web browser typically set this header to contains text/html. Other applications prefer to work with formats like JSON or XML (for the young kids out there: XML is a weird markup language we had to work with before we got JSON).

The actual content of the header can be quite complex, for example, my browser sends:

text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8

This is a list of formats in the which they are preferred by the browser (see content negotiation for details).

Let’s see how use the Accept header to decide when to return HTML from Spark (spoiler alert: it is easy!)


private static boolean shouldReturnHtml(Request request) {
    String accept = request.headers("Accept");
    return accept != null && accept.contains("text/html");
}


// ...jumping down in the code, where we define routes

// get all post (using HTTP get method)
get("/posts", (request, response) -> {
    if (shouldReturnHtml(request)) {
        // produce HTML
    } else {
        // produce JSON
    }
});

In this example we either get a request specifying text/html in the Accept header or we provide JSON in all other cases. It could make sense to return an error instead of always producing JSON: if someone is asking for XML they are not going to be able to process the JSON we are sending back to them.

Producing HTML programmatically

Let’s see how we could generate HTML from pure Java. We are going to use j2html. j2html is a simple Java to HTML builder made by David Åse, the person responsible for the Spark websites (if you are interested in joining the Spark project, you can read about how David got involved in this interview).

Let’s start by adding the dependency in our POM file:


<dependency>
    <groupId>com.j2html</groupId>
    <artifactId>j2html</artifactId>
    <version>1.2.0</version>
</dependency>

Now we can use j2html and some Java 8 magic to transform a list of posts into a page that we can render:


return body().with(
      h1("My wonderful blog"),
      div().with(
          model.getAllPosts().stream().map((post) ->
                div().with(
                        h2(post.getTitle()),
                        p(post.getContent()),
                        ul().with(post.getCategories().stream().map((category) ->
                              li(category)).collect(Collectors.toList())
                        )
                )
          ).collect(Collectors.toList())
      )
).render();

Let’s take a look at what is happening here:

  • we start from the body of the page and we insert two elements in it: a header (h1) and a div which will contain all the posts
  • we then take all the posts and map each post to the corresponding piece of HTML code
  • each single post is mapped to a div which contains a header (h2) for the title a paragraph (p) for the content and a list (ul) for the categories
  • each single category is mapped to a list element (li)

I think j2html is great when prototyping HTML pages because you don’t have to deal with the hassle of setting up a template engine. In addition to that is very useful when you need to generate small pieces of HTML to be used to compose larger pages. However if you want to build complex layouts you may want to use templates to achieve a stronger separation between logic and presentation. In the next paragraph we take a look such a template engine.

The example using j2html can be found in the GitHub repository under the tag j2html.

Producing HTML using the FreeMarker template engine

For large HTML content you may want to use templates. The basic idea is the you can let a designer create a sample page and then replace the sample content with placeholders that are going to be dynamically replaced by the actual content at each request.
Spark has native support for a lot of template engines (see here for the complete list).

One thing that I like about FreeMarker is that it is quite flexible. For example we can use different strategies to find the templates. The two most common approaches are:

  • pack the templates inside the application (as part of the jar/war produced and deployed)
  • leave the templates outside the application

The first strategy makes deployment easier: The package contains everything, including the templates. The second one allows for modifying the templates without having to re-package and redeploy the application.

I prefer the first option (at least when templates are reasonably stable) so we are going to see how we can store the templates as resource files.

Let’s start by creating a file named posts.ftl under the directory src/main/resources. The template will not contain dynamic parts just this simple content:


<html>
<head>
</head>
<body>
    <h1>The marvellous blog of mine</h1>
</body>
</html>

Now we have to configure FreeMarker to look for templates among the resource files. It is as simple as writing a few lines of code before you start defining your routes:


FreeMarkerEngine freeMarkerEngine = new FreeMarkerEngine();
Configuration freeMarkerConfiguration = new Configuration();
freeMarkerConfiguration.setTemplateLoader(new ClassTemplateLoader(BlogService.class, "/"));
freeMarkerEngine.setConfiguration(freeMarkerConfiguration);

Now we have to specify which template to use for a specific route


// get all post (using HTTP get method)
get("/posts", (request, response) -> {
    if (shouldReturnHtml(request)) {
        response.status(200);
        response.type("text/html");
        Map<String, Object> attributes = new HashMap<>();
        attributes.put("posts", model.getAllPosts());
        return freeMarkerEngine.render(new ModelAndView(attributes, "posts.ftl"));
    } else {
        response.status(200);
        response.type("application/json");
        return dataToJson(model.getAllPosts());
    }
});

And this should be the result:

Screenshot of blog

How to pass data to the template

Cool, we are able to load and display a template but so far the template does not contain any dynamic content: it is just producing the same page whatever content we have in our blog. Let’s correct this.

In the code to render the template we used a map:


Map attributes = new HashMap<>();
attributes.put("posts", model.getAllPosts());

return freeMarkerEngine.render(new ModelAndView(attributes, "posts.ftl"));

This is the mechanism to pass data to the template. As you can see we are already passing a list of posts to the template, now let’s see how to display them:


<#list posts as post>
    <div class="post">
        <h2>${post.title}</h2>
        <p>${post.content}</p>
        
        <h3>Categories:</h3>
        <ul class="categories">
            <#list post.categories as category>
                <li>${category}</li>
            </#list>
        </ul>
        <i>Published on the ${post.publishing_date}</i>
    </div>
</#list>

What’s happening here?

  • we start by iterating on the posts (posts is a key in the map we passed to the FreeMarker engine, so we can access it here)
  • for each post we display title, content and publishing_date
  • we iterate on the categories of the single post and display them

And this should be the result, displaying a couple of posts:

Screenshot of blog

The example using FreeMarker is present in the GitHub repository under the tag freemarker.

Conclusions

As we have seen in this post Spark can be easily integrated with template engines and FreeMarker is a decent choice. I personally think that Spark is great for RESTful services but it does a pretty good job also for common user-facing web applications and it can be easily used for mixed applications (RESTful service + HTML interface, as we have seen in this post).

There are improvements to be done to use templates in a large application. For example you may want to define a common structure (header, navigation column, footer) to be used in all the different templates, without having to maintain copies. To understand how to do this and more advanced topics I suggest to take a look at the FreeMarker documentation.