In the first two parts of this Java REST API Tutorial mini series, you accessed the NYTimes API (Part 1), and you integrated the NYTimes API within a Spring Boot application (Part 2).
Now that you have a functioning service and models to map your data, this lesson will help you further your project by creating the necessary classes to display the received API data. You will build a Thymeleaf front end that utilizes Bootstrap CSS to help things look nice and tidy.
Java REST API Tutorial Part 3: Display the NYTimes API Data
Adding Data to the Model
The next step in this project is to create your controller. At this point, the controller can be super simple, with only a / endpoint, referencing the homepage of the application. Since you are working with the New York Times, go ahead and base this class on your Article class. Here is the ArticleController:
@Controller
public class ArticleController {
@Autowired
ArticleService articleService;
@GetMapping("/")
public String home(Model model) {
model.addAttribute("articleList", articleService.getMostPopular());
return "index";
}
}
This class autowires an instance of your ArticleService class, and utilizes its getMostPopular() method to retrieve data which is then added to the model. The view "index" is returned.
Template Files
Create your index.html template (in resources/templates) as follows:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<title>New York Times API Demo</title>
<div th:replace="fragments :: header"/>
</head>
<body>
<div th:replace="fragments :: navigation"/>
<div class="container">
<div class="bg-light p-4 rounded m-2">
<h1 class="display-4">Today's Most Popular Stories</h1>
<th:block th:unless="${articleList.isEmpty()}">
<div class="row justify-content-center">
<div class="col-auto" th:each="article: ${articleList}">
<div class="card" style="width: 18rem; height: 34rem;">
<img th:unless="${article.imageUrl == null}" th:src="${article.imageUrl}" class="card-img-top">
<div class="card-body">
<h5 class="card-title" th:text="${article.title}">title</h5>
<p class="card-text" th:text="${article.summary}">summary</p>
<a th:href="${article.url}" class="btn btn-primary">Read More..</a>
</div>
</div>
</div>
</div>
</th:block>
</div>
</div>
<div th:replace="fragments :: footer"/>
</body>
</html>
Ok, a lot going on here! First, th:unless is used to display the content inside only if the condition is false. In this case, articleList.isEmpty(). If articleList contains entries, th:each iterates the list and creates a Bootstrap column for each. Inside this column, a Bootstrap "card" is created, utilizing data from the Article. At this point, you have probably noticed that Bootstrap CSS isn't even included here, or is it? In the HTML head section your find the following line:
<div th:replace="fragments :: header"/>
This is utilizing Thymeleaf Layout Dialect in the form of HTML fragments. Basically, you are instructing Thymeleaf to replace this div with a block of code named "header", from the template file fragments.html. Below you will find this file to understand better how this works. Create this template in resources/templates.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<div th:fragment="header">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</div>
</head>
<body>
<div th:fragment="navigation">
<nav class="navbar navbar-expand-lg navbar-light bg-light bg-gradient">
<div class="container-fluid">
<a th:href="@{/}" class="navbar-brand">NY Times API Demo</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<li class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" th:href="@{/}">Home</a>
</li>
</ul>
</li>
</div>
</nav>
</div>
<div th:fragment="footer">
<div class="container">
<footer>
<p>© 2023. All rights reserved.</p>
</footer>
</div>
</div>
</body>
</html>
The "header" fragment includes some metadata, along with the Bootstrap CSS distribution. The "navigation" fragment creates a Bootstrap navbar, that will be placed at the top of each page the includes it. As you can see, Thymeleaf fragments allow you to easily inject HTML fragments rather than duplicating common elements on each page.
Fire it Up!
With your controller and templates in place, it's time to test your work. Fire up the application, and navigate to http://localhost:8080. If all went as planned, you should see output similar to below:
At this point, set a breakpoint in your service method getMostPopular(), and the controller. Spend some time stepping through each in order to really visualize what is taking place. Aside from varying security implementations, consuming API data can be reliably executed exactly as you have done here.
Summary: REST API Tutorial Part 3 - Display Data with Spring MVC and Thymeleaf
In this lesson you built a controller and template in order to display the data you receive from the Most Popular API. You also learned to utilize Thymeleaf HTML fragments in your templates, reducing duplicate and boilerplate code. Get ready for the lab! It will push your new knowledge even further.