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.
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.
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.
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.
We will create a simple example of hypermedia-driven REST service with 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>
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.
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.
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 + "]"; } }
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.
Strategy Design Patterns We can easily create a strategy design pattern using lambda. To implement…
Decorator Pattern A decorator pattern allows a user to add new functionality to an existing…
Delegating pattern In software engineering, the delegation pattern is an object-oriented design pattern that allows…
Technology has emerged a lot in the last decade, and now we have artificial intelligence;…
Managing a database is becoming increasingly complex now due to the vast amount of data…
Overview In this article, we will explore Spring Scheduler how we could use it by…