Donny's Programming Blog

Making an SSR Web App with Spring Boot (Part 2)

March 02, 2019

This is Part 2 of 5. If you don’t have a project setup, please see part 1

Introduction

Now that we have a Spring project setup, it’s time to actually start building out a project. For the rest of this series, we’ll be getting into the Model-View-Controller (MVC) structure that exists in all Spring applications. We’re going to start with the ‘C’ in MVC, Controllers.

Controllers

In an MVC application, Controllers are the pieces that direct traffic for us. Whenever we make a request to our application, the Controller is what dictates how our application should respond.

We make requests to web applications by navigating to different URIs from our browser. Anytime we go to a new page, we are requesting whatever data corresponds to that endpoint in the application. Let’s begin by making a simple Controller that will handle requests to the root of our application.

We’ll first need to make a new package to house our controllers. I’ll store mine at com.donhamiltoniii.literateoctodollop.controllers. Next we’ll need a new Class called HomeController. Our HomeController will have the following setup (your package should correspond to your project):

package com.donhamiltoniii.literateoctodollop.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

	@GetMapping("/")
	public String home() {
		return "index";
	}
}

Let’s go through this a bit. First, we need the @Controller annotation to make spring aware that this is indeed a component in our Spring application. Now whenever our application starts, this Class will be included. The @GetMapping annotation handles any HTTP GET requests that come to our application from the specified route (”/”). The return value of our home() method is the String index. This represents the corresponding View that our controller will render when this method executes.

But what’s a View…

Views

On to the ‘V’ in our MVC application, Views. Our views are made up of Thymeleaf templates that ultimately render static HTML files in our browser. We use Thymeleaf to dynamically render content in our application. For example, we can repeat HTML elements for every book that we might have in our application. Our View templates are housed inside of the src/main/resources/templates directory. Let’s get a basic template set up to render in our browser in src/main/resources/index.html:

<!DOCTYPE html>
<html xmlns:th="https://thymeleaf.org">
  <head>
    <title>Literate Octo Dollop</title>
  </head>
  <body>
    <h1>Welcome To Literate Octo Dollop</h1>
    <h2>Home of the most informative books available</h2>

    <p>Content Coming Soon...</p>
  </body>
</html>

Clearly there isn’t much happening here yet:

Rendered HTML

One piece of note, however, is the xmlns:th="https://thymeleaf.org" attribute in the html tag. This has to be included for us to use Thymeleaf syntax later. Now that we have something rendering, let’s add a new Controller and another View template.

Books

The next Controller we’re going to make is going to handle anything related to books. So, let’s make a new BookController Class in our controllers package.

package com.donhamiltoniii.literateoctodollop.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/books")
public class BookController {

	@GetMapping({ "", "/", "/index" })
	public String getAllBooks() {
		return "books/all";
	}
}

We now have a new Controller that will handle any route that starts with /books. So whether we go to localhost:8080/books, localhost:8080/books/1, or localhost:8080/books/add, this Controller will handle those requests. Now let’s make a View template at the corresponding src/main/resources/templates/books/all.html:

<!DOCTYPE html>
<html xmlns:th="https://thymeleaf.org">
  <head>
    <title>Literate Octo Dollop: Books</title>
  </head>
  <body>
    <h1>Literate Octo Dollop</h1>
    <h2>Books</h2>

    <p>Content Coming Soon...</p>
  </body>
</html>

For now, this content is very similar to our homepage. But we want to display books here and in that case, we need some books. For that we’re going to need to talk about the final letter in our MVC application, ‘M’.

Models

Models represent any code that has to do with our data. This can be the Classes that structure our data as well as the repositories and databases that house them. Let’s begin by making our first Entity:

package com.donhamiltoniii.literateoctodollop.models;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Book {

	@Id
	@GeneratedValue
	private Long id;
	private String title;
	private String author;
	private String genre;

	// This is a hook for JPA - We will never call this Constructor
	public Book() {
	}

	public Book(String title, String author, String genre) {
		this.title = title;
		this.author = author;
		this.genre = genre;
	}

	public Long getId() {
		return id;
	}

	public String getTitle() {
		return title;
	}

	public String getAuthor() {
		return author;
	}

	public String getGenre() {
		return genre;
	}

	@Override
	public String toString() {
		return "Book [id=" + id + ", title=" + title + ", author=" + author + ", genre=" + genre + "]";
	}

}

We’ll also need to keep track of all of our books. For that we’ll need a repository. So let’s make that:

package com.donhamiltoniii.literateoctodollop.repositories;

import org.springframework.data.repository.CrudRepository;

import com.donhamiltoniii.literateoctodollop.models.Book;

public interface BookRepository extends CrudRepository<Book, Long> {

}

Spring does pretty much all of the heavy lifting for us here. The CrudRepository comes with a bunch of useful methods for managing our data.

Initializer

So now we have a Model for any books that we make. So let’s make a new Initializer Class that will allow us to make some books that we can access with our application.

And Initializer Class allows you to make some starting data in your application so that you have some material to play around with without having to rely on a user supplying that information for you. Let’s have a look:

package com.donhamiltoniii.literateoctodollop;

import javax.annotation.Resource;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;

import com.donhamiltoniii.literateoctodollop.models.Book;
import com.donhamiltoniii.literateoctodollop.repositories.BookRepository;

@Service
public class Initializer implements CommandLineRunner {

	@Resource
	BookRepository bookRepo;

	@Override
	public void run(String... args) throws Exception {
		bookRepo.save(new Book("J2EE Development withou EJB", "Rod Johnson", "Tech"));
		bookRepo.save(new Book("Spring Boot In Action", "Craig Walls", "Tech"));
		bookRepo.save(new Book("Head First Design Patterns", "Elisabeth Robson", "Tech"));
	}

}

Now we have some books available to us. Let’s look at how we can render them in our application.

Back to the Controller

So in order to view this data in our application, we need some way to inject it into our template that we made earlier. Luckily fore us, Spring has an interface built into it that does just that.

package com.donhamiltoniii.literateoctodollop.controllers;

import javax.annotation.Resource;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.donhamiltoniii.literateoctodollop.repositories.BookRepository;

@Controller
@RequestMapping("/books")
public class BookController {

	@Resource
	BookRepository bookRepo;

	@GetMapping({ "", "/", "/index" })
	public String getAllBooks(Model model) {
		model.addAttribute("books", bookRepo.findAll());
		return "books/all";
	}
}

The Model interface allows us to pull data from our repository and pass it to our templates for dynamic rendering. The way it works is using a key value pair to identify the data that our Controller will send to our View template from our Model. In our case it’s books. Now let’s take a look back in our template to see what this relationship looks like on the other side.

<!DOCTYPE html>
<html xmlns:th="https://thymeleaf.org">
  <head>
    <title>Literate Octo Dollop: Books</title>
  </head>
  <body>
    <h1>Literate Octo Dollop</h1>
    <h2>Books</h2>

    <ul>
      <li th:each="books : ${books}">
        <h3>
          <a th:href="@{|/books/${book.id}|}" th:text="${book.title}">
            Default Title
          </a>
        </h3>
        <h4 th:text="|By: ${book.author}|">Default Author</h4>
        <small th:text="${book.genre}">Default Genre</small>
      </li>
    </ul>
  </body>
</html>

We now have a list of all of the books we added in our Initializer!

We’ve come a long way. We have the ability to display dynamic content in our web app now. Let’s dive into the next section where we will dig into looking at a single book as well as letting our users add books from our browser environment. Join me here for part 3


Don Hamilton III

Written by Don Hamilton III who lives and works in Columbus, OH teaching folx to program at We Can Code IT. You should follow him on Twitter