HATEOAS is a principle to achieve the REST specification, by following the HATEOAS specification, we can solve the actual code to achieve a variety of individual problems. As the most popular framework for java Spring will of course not be absent from the integration of HATEOAS.

This article will explain how to use HATEOAS in SpringBoot through a specific example.

Our goal

The HATEOAS rule returns data with links. Let’s take the familiar Book example to demonstrate this HATEOAS by first creating a Book entity.

1
2
3
4
5
6
7
8
9
@Data
@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
}

We would like to be able to access the detailed data of Book through the following link.

1
GET /book/1

The returned data are as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
    "content": {
        "id": 1,
        "title": "The Hobbit"
    },
    "_links": {
        "self": {
            "href": "http://localhost:8080/book/1"
        }
    }
}

You can see that in the returned data, in addition to content containing information about the book, there is also a _links attribute, which indicates the links to resources related to the book.

Build Entity and Repository

Before doing any data, we need to build the corresponding data, that is, the entity and the corresponding data operations. For simplicity, we use H2’s in-memory database.

We need to configure the following in application.properties.

1
2
3
4
5
6
7
spring.jpa.hibernate.ddl-auto=validate

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

Then configure the corresponding repository :

1
2
3
4
5
6
7
public interface BookRepository extends CrudRepository<Book, Long> {
    long deleteByTitle(String title);

    @Modifying
    @Query("delete from Book b where b.title=:title")
    void deleteBooks(@Param("title") String title);
}

Also, you need to place schema.sql for table creation and data.sql for inserting data in resources so that the corresponding data can be created automatically when the application starts.

If you want to do it yourself, you can also implement the add link operation. But that would be too complicated, luckily we have Spring. to use HATEOAS in Spring, the following configuration is required.

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>

If we want to build HATEOAS for Book, then we can just build a class that inherits RepresentationModel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class BookModel extends RepresentationModel<BookModel> {

    private final Book content;

    @JsonCreator
    public BookModel(@JsonProperty("content") Book content) {
        this.content = content;
    }

    public Book getContent() {
        return content;
    }
}

In the above example, we wrapped a Book object with RepresentationModel and set it to json’s content property.

Build the Controller

With the RepresentationModel in place, we can use it to build the HATEOAS response.

Let’s look at the following example.

1
2
3
4
5
6
7
@RequestMapping("/book/{id}")
public HttpEntity<Book> getBook(@PathVariable("id") Long id) {
    Book book= bookRepository.findById(id).get();
    BookModel bookModel = new BookModel(book);
    bookModel.add(linkTo(methodOn(BookController.class).getBook(id)).withSelfRel());
    return new ResponseEntity(bookModel, HttpStatus.OK);
}

In the above example, we used @RequestMapping to construct an HTTP request to find the corresponding Book data from the database by passing in the book id.

Then we pass it into the BookModel and build the RepresentationModel, which can return the object directly. But we also need to add some links to it.

We use bookModel.add to add the appropriate links and use the linkTo method to generate the appropriate links.

Finally, the RepresentationModel is returned.

When we request /book/1, we will get the first json value we want to get. Isn’t it easy to use HATEOAS?

The meaning of HATEOAS

HATEOAS comes with the corresponding resource links, through a resource you can get other resources that can be accessed from this resource, just like a visit to a page, you can then go through this page to access other pages.

So the significance of HATEOAS is that we only need to access one resource to traverse all the resources.

Let’s experience accessing a resource by testing it.

First, let’s directly access the resource /book/1 to confirm the result.

1
2
3
4
5
@Test
void envEndpointNotHidden() throws Exception {
    mockMvc.perform(get("/book/1"))
            .andExpect(jsonPath("$.content.title").value("The Hobbit"));
}

The link traversal is then performed through the Traverson class provided by Spring HATEOAS.

1
2
3
4
5
6
@Test
void envEndpointNotHidden() throws Exception {
    Traverson traverson = new Traverson(new URI("http://localhost:" + this.port + "/book/1"), MediaTypes.HAL_JSON);
    String bookTitle = traverson.follow("self").toObject("$.content.title");
    assertThat(bookTitle).isEqualTo("The Hobbit");
}

Reference http://www.flydean.com/00042-springboot-hateoas/