Spring HATEOAS

Spring HATEOAS- a Hypermedia-Driven RESTful Web Service

In this Spring HATEOAS tutorial we will discuss about an another way of use of Restfull API. HATEOAS (Hypermedia as the Engine of Application State)- a Hypermedia-Driven RESTful Web Service. It is a constraint of the REST application architecture.

Table of Contents

  1. Introduction
  2. What is HATEOAS?
  3. What is need of HATEOAS?
  4. Features of HATEOAS
  5. Build a a Hypermedia-Driven RESTful Web Service
    1. Maven Dependency for Spring HATEOAS
    2. Implementation With LinkBuilder API
    3. ResourceSupport Implementation
  6. Summary


1. Introduction

Here we using Spring HATEOAS for creating very simple example of restful web service. Before that lets take understanding about HATEOAS (Hypermedia as the Engine of Application State) is a constraint of the REST application architecture.

2. What is HATEOAS?

HATEOAS is an another way to creating RESTful web services such that the client can dynamically discover the next or previous actions available to it at run-time from the server. These actions forward to the client as the Response body or Response Header in the for the links. HATEOAS is considered the final level of REST. This means that each link is presumed to implement the standard REST verbs of GET, POST, PUT, and DELETE.

3. What is need of HATEOAS?

Restful web service is very popular due to light weight and its six contraints. But there is drawback of Restful web service for large system because of each services would have a seperate URL to be defined. It is not easy to remember and maintain the complete list of services and state transition by the clients.

HATEOAS approach comes into picture with Restful API to overcome this problem. For All the client should require to get started is an initial URI, and set of standardized media types. Once it has loaded the initial URI, all future application state transitions will be driven by the client selecting from choices provided by the server. A hypermedia-driven site provides information to navigate the site’s REST interfaces dynamically by including hypermedia links with the responses.

4. Features

  • Model classes for link, resource representation models
  • Link builder API to create links pointing to Spring MVC controller methods
  • Support for hypermedia formats like HAL


5. Build a a Hypermedia-Driven RESTful Web Service

We will create a simple example of hypermedia-driven REST service with Spring HATEOAS.

5.1 Maven Dependency for Spring HATEOAS

If you are using Maven, please add these dependencies to your pom.xml file.

<dependencies>
    <dependency>
        <groupId>org.springframework.hateoas</groupId>
        <artifactId>spring-hateoas</artifactId>
        <version>0.22.0.RELEASE</version>
    </dependency>
</dependencies>

5.2 Implementation With LinkBuilder API

A Simple HATEOAS Application

A simple example of a Spring HATEOAS project is freely available on Github

https://github.com/DOJ-SoftwareConsultant/spring-hateoas-rest-ws

This simple example consists of 2 media types: ‘Account‘ and ‘AccountHolder

AccountController.java

/**
 * 
 */
package com.doj.hateoas.ws.accounts;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Resource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Dinesh.Rajput
 *
 */
@RestController
public class AccountController {

 protected Logger logger = Logger
   .getLogger(AccountController.class.getName());
 
 @Autowired
 AccountRepository accountRepository;
 
 @RequestMapping("/accounts")
 public List<Resource<Account>> all() {
  logger.info("accounts all() invoked");
  List<Account> accounts = accountRepository.getAllAccounts();
  List<Resource<Account>> resources = new ArrayList<Resource<Account>>();
  for (Account account : accounts) {
          resources.add(getAccountResource(account));
       }
  logger.info("accounts all() found: " + accounts.size());
  return resources;
 }
 
 @RequestMapping(value= "/accounts/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
 public Resource<Account> byId(@PathVariable("id") Long id) {
  logger.info("accounts byId() invoked: " + id);
  Account account = accountRepository.getAccount(id.toString());
  Resource<Account> resource = new Resource<Account>(account);
  resource.add(linkTo(methodOn(AccountController.class).all()).withRel("accounts"));
  resource.add(linkTo(methodOn(AccountController.class).findAccountHolderById(account.getAccountId(), account.getAccountHolder().getUserId())).withRel("accountHolder"));
  logger.info("accounts byId() found: " + account);
  return resource;
 }
 
 @RequestMapping(value= "/accounts/{id}/{userId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
 public Resource<AccountHolder> findAccountHolderById(@PathVariable("id") Long id, @PathVariable("userId") Long userId) {
  logger.info("accounts findAccountHolderById() invoked: " + id);
  Account account = accountRepository.getAccount(id.toString());
  AccountHolder accountHolder = account.getAccountHolder();
  Resource<AccountHolder> resource = new Resource<AccountHolder>(accountHolder);
  resource.add(linkTo(methodOn(AccountController.class).byId(account.getAccountId())).withRel("account"));
  logger.info("accounts findAccountHolderById() found: " + account);
  return resource;
 }
 
 private Resource<Account> getAccountResource(Account account) {
  Resource<Account> resource = new Resource<Account>(account);
  resource.add(linkTo(methodOn(AccountController.class).byId(account.getAccountId())).withSelfRel());
  resource.add(linkTo(methodOn(AccountController.class).findAccountHolderById(account.getAccountId(), account.getAccountHolder().getUserId())).withRel("accountHolder"));
  return resource;
 }
 
}

Look at the first two import statements, those are static imports as part of the spring hateoas api. linkTo and methodOn are used for adding the link to the response.

The entry point to our API is :
http://localhost:1111/accounts

[
{
accountId: 1000,
accountHolder: {
userId: 5115,
name: "Arnav",
address: "Noida"
},
amount: 1039.13,
ifscCode: "AA992QA",
links: [
{
rel: "self",
href: "http://localhost:1111/accounts/1000"
},
{
rel: "accountHolder",
href: "http://localhost:1111/accounts/1000/5115"
}
]
},
{
accountId: 2000,
accountHolder: {
userId: 2089,
name: "Anamika",
address: "Noida"
},
amount: 1239.43,
ifscCode: "AB966QJ",
links: [
{
rel: "self",
href: "http://localhost:1111/accounts/2000"
},
{
rel: "accountHolder",
href: "http://localhost:1111/accounts/2000/2089"
}
]
},
{
accountId: 3000,
accountHolder: {
userId: 1286,
name: "Dinesh",
address: "Noida"
},
amount: 3339.61,
ifscCode: "AD912SA",
links: [
{
rel: "self",
href: "http://localhost:1111/accounts/3000"
},
{
rel: "accountHolder",
href: "http://localhost:1111/accounts/3000/1286"
}
]
}
]

This will basically list all the accounts available at our repository.
This response not only has the account’s detail, but includes the self-linking URL where that account is located.

  • rel means relationship. In this case, it’s a self-referencing hyperlink. More complex systems might include other relationships. For example, an account might have a “rel”:”accountHolder” relationship, linking the order to its customer.
  • href is a complete URL that uniquely defines the resource.

It provides links to any account detail of our api telling it the URLs it can use to:

http://localhost:1111/accounts/{id}

{
accountId: 1000,
accountHolder: {
userId: 5115,
name: "Arnav",
address: "Noida"
},
amount: 1039.13,
ifscCode: "AA992QA",
_links: {
accounts: {
href: "http://localhost:1111/accounts"
},
accountHolder: {
href: "http://localhost:1111/accounts/1000/5115"
}
}
}

It also provides links to any account holder of any account detail of our api telling it the URLs it can use to:

http://localhost:1111/accounts/{id}/{accountHolderId}

{
userId: 5115,
name: "Arnav",
address: "Noida",
_links: {
account: {
href: "http://localhost:1111/accounts/1000"
}
}
}

This account holder detail api has link for associated account detail for this user

And account detail API has two links one for all account list and other for account holder details.

5.3 ResourceSupport Implementation

ResourceSupport is the representation of resources. This class allows us to add the links and manage it.

/**
 * 
 */
package com.doj.hateoas.ws.accounts;

import java.io.Serializable;

import org.springframework.hateoas.ResourceSupport;

/**
 * @author Dinesh.Rajput
 *
 */
public class Account extends ResourceSupport implements Serializable {
 
 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 private Long accountId;
 private AccountHolder accountHolder;
 private Double amount;
 private String ifscCode;
 public Long getAccountId() {
  return accountId;
 }
 public void setAccountId(Long accountId) {
  this.accountId = accountId;
 }
 public AccountHolder getAccountHolder() {
  return accountHolder;
 }
 public void setAccountHolder(AccountHolder accountHolder) {
  this.accountHolder = accountHolder;
 }
 public Double getAmount() {
  return amount;
 }
 public void setAmount(Double amount) {
  this.amount = amount;
 }
 public String getIfscCode() {
  return ifscCode;
 }
 public void setIfscCode(String ifscCode) {
  this.ifscCode = ifscCode;
 }
 public Account(Long accountId, AccountHolder accountHolder, Double amount, String ifscCode) {
  super();
  this.accountId = accountId;
  this.accountHolder = accountHolder;
  this.amount = amount;
  this.ifscCode = ifscCode;
 }
 @Override
 public String toString() {
  return "Account [accountId=" + accountId + ", accountHolder=" + accountHolder + ", amount=" + amount
    + ", ifscCode=" + ifscCode + "]";
 }
}

6. Summary

Here we have been learned about Spring HATEOAS one of different approach to creating Restful web service. And also a build an application to describe HATEOAS concept.

 

Dinesh Rajput

Dinesh Rajput is the chief editor of a website Dineshonjava, a technical blog dedicated to the Spring and Java technologies. It has a series of articles related to Java technologies. Dinesh has been a Spring enthusiast since 2008 and is a Pivotal Certified Spring Professional, an author of a book Spring 5 Design Pattern, and a blogger. He has more than 10 years of experience with different aspects of Spring and Java design and development. His core expertise lies in the latest version of Spring Framework, Spring Boot, Spring Security, creating REST APIs, Microservice Architecture, Reactive Pattern, Spring AOP, Design Patterns, Struts, Hibernate, Web Services, Spring Batch, Cassandra, MongoDB, and Web Application Design and Architecture. He is currently working as a technology manager at a leading product and web development company. He worked as a developer and tech lead at the Bennett, Coleman & Co. Ltd and was the first developer in his previous company, Paytm. Dinesh is passionate about the latest Java technologies and loves to write technical blogs related to it. He is a very active member of the Java and Spring community on different forums. When it comes to the Spring Framework and Java, Dinesh tops the list!

Share
Published by
Dinesh Rajput

Recent Posts

Strategy Design Patterns using Lambda

Strategy Design Patterns We can easily create a strategy design pattern using lambda. To implement…

2 years ago

Decorator Pattern using Lambda

Decorator Pattern A decorator pattern allows a user to add new functionality to an existing…

2 years ago

Delegating pattern using lambda

Delegating pattern In software engineering, the delegation pattern is an object-oriented design pattern that allows…

2 years ago

Spring Vs Django- Know The Difference Between The Two

Technology has emerged a lot in the last decade, and now we have artificial intelligence;…

2 years ago

TOP 20 MongoDB INTERVIEW QUESTIONS 2022

Managing a database is becoming increasingly complex now due to the vast amount of data…

2 years ago

Scheduler @Scheduled Annotation Spring Boot

Overview In this article, we will explore Spring Scheduler how we could use it by…

3 years ago