Hypermedias com Spring HETEOAS
By: Daiene Lima
Tags: Java, Spring Boot, Hateoas

O que é HATEOAS ?

HATEOAS ou (Hypertext as the Engine of Application State) é um modelo que permite que você navegue entre seus endpoints de forma dinâmica através de links e URLs e sem precisar do conhecimento prévio sobre a API. Nesse caso o cliente descobrirá as URLs conforme navega entre seus recursos.

Uma boa forma de comparação de HATEOAS é o Hypertext ou Hipertexto que é comum para quem navega na internet diariamente. Quando estamos pesquisando por algo na internet ou lendo alguma notícia e nos deparamos com alguma informação que nos direciona para outro link.

O mesmo conceito pode ser aplicado a API em que a aplicação consumidora terá um link inicial e a partir dele terá acesso a outros recursos caso necessário.

Spring-HATEOAS é uma biblioteca de APIs que podemos usar para criar representações REST que seguem o padrão HATEOAS.

Vamos construir uma aplicação simples com o objetivo de mostrar as vantagens de usar o HATEOAS

Primeiro adicionamos a dependência do spring-HATEOAS

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-hateoas</artifactId>

</dependency>

Criaremos um model

@Entity

public class Employee {



private @Id @GeneratedValue Long id;

private String name;

private String role;



public Employee() {}

public Employee(String name, String role) {

this.name = name;

this.role = role;

}

//gets and sets

Depois vamos implementar a seguinte lógica no nosso controller

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;



import java.util.List;

import java.util.Optional;

import java.util.stream.Collectors;



import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.hateoas.CollectionModel;

import org.springframework.hateoas.EntityModel;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.DeleteMapping;

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

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.PutMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RestController;



import com.example.demo.exception.EmployeeNotFoundException;

import com.example.demo.model.Employee;

import com.example.demo.repository.EmployeeRepository;



@RestController

public class EmployeeController {



@Autowired

private EmployeeRepository repository;





@GetMapping("/employees")

public ResponseEntity<CollectionModel<EntityModel<Employee>>> all() {



List<EntityModel<Employee>> employees = repository.findAll().stream()

.map(employee -> EntityModel.of(employee,

linkTo(methodOn(EmployeeController.class).one(employee.getId())).withSelfRel()))

.collect(Collectors.toList());



return ResponseEntity.ok(CollectionModel.of(employees));

}



@GetMapping("/employees/{id}")

public ResponseEntity<EntityModel<Employee>> one(@PathVariable Long id) {



Employee employee = repository.findById(id)

.orElseThrow(() -> new EmployeeNotFoundException(id));



return ResponseEntity.of(Optional.of(EntityModel.of(employee,

linkTo(methodOn(EmployeeController.class).all()).withRel("employees"))));

}

}

O methodOn() obtém o mapeamento do método fazendo uma invocação fictícia do método de destino.

O método linkTo() inspeciona a classe do controlador e obtém seu mapeamento raiz e finalmente, o withSelfRel() qualifica a relação como um link próprio.

Nesse exemplo usamos o CollectionModel ele auxilia na criação de um wrapper para uma coleção de entidades.

No final temos o seguinte resultado

Se listarmos todos os employees, ele vai retornar no json um link para acessar cada employee individualmente, caso seja necessário.

localhost:8080/employees

Se fizemos uma busca por apenas um employee ele nos retorna no json um link para acessar todos os employees

localhost:8080/employees/1

Esse é um exemplo simples onde vemos que o cliente pode ter um único ponto de entrada para a aplicação e outras ações podem ser tomadas com base na resposta.

Nessa aplicação eu mostrei como Spring HATEOAS promove a descoberta de API em um serviço rest.

link do repositório com o código fonte projeto-exemplo.