Following example shows how to link multiple resources using Spring HATEOAS API.
Example
Domain classes
public class Employee {
private long employeeId;
private String name;
private Dept dept;
.............
}
public class Dept {
private long deptId;
private String name;
.............
}
public class Task {
private long taskId;
private String name;
private Employee employee;
.............
}
Controllers
We are creating following resources:
- /employees
- /employees/{employeeId}
- /employees/{employeeId}/tasks
- /tasks
- /tasks/{taskId}
- /depts
- /depts/{deptId}
package com.logicbig.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
@RestController
@RequestMapping("/employees")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@GetMapping
public Resources<Resource<Employee>> getEmployees() {
List<Employee> employeeList = employeeService.getAllEmployees();
List<Resource<Employee>> employeeResources =
employeeList.stream()
.map(EmployeeController::createEmployeeResource)
.collect(Collectors.toList());
Link selfRel = linkTo(methodOn(EmployeeController.class)
.getEmployees()).withSelfRel();
return new Resources<>(employeeResources, selfRel);
}
@GetMapping("/{employeeId}")
public Resource<Employee> getEmployeeById(@PathVariable long employeeId) {
Employee employee = employeeService.getEmployeeById(employeeId);
Link selfLink = linkTo(methodOn(EmployeeController.class)
.getEmployeeById(employeeId)).withSelfRel();
return new Resource<>(employee, selfLink, createTasksLink(employeeId),
createDeptLink(employee.getDept().getDeptId()));
}
private static Resource<Employee> createEmployeeResource(Employee e) {
Link employeeLink = linkTo(methodOn(EmployeeController.class)
.getEmployeeById(e.getEmployeeId())).withSelfRel();
return new Resource<>(e, employeeLink);
}
private static Link createTasksLink(long employeeId) {
return linkTo(methodOn(TaskController.class)
.getEmployeeTasks(employeeId)).withRel("tasks");
}
private Link createDeptLink(long deptId) {
return linkTo(methodOn(DeptController.class)
.getDept(deptId)).withRel("dept");
}
}
@RestController
public class TaskController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/employees/{employeeId}/tasks")
public Resources<Resource<Task>> getEmployeeTasks(@PathVariable long employeeId) {
List<Task> tasks = employeeService.getTasksByEmployeeId(employeeId);
List<Resource<Task>> taskResources = tasks.stream()
.map(TaskController::createTaskResource)
.collect(Collectors.toList());
return new Resources<>(taskResources);
}
@GetMapping("/tasks/{taskId}")
public Resource<Task> getTask(@PathVariable long taskId) {
Task task = employeeService.getTaskByTaskId(taskId);
Link selfRel = linkTo(methodOn(TaskController.class).getTask(taskId)).withSelfRel();
Resource<Task> taskResource = new Resource<>(task, selfRel);
taskResource.add(createEmployeeLink(task.getEmployee().getEmployeeId()));
return taskResource;
}
@GetMapping("/tasks")
public Resources<Resource<Task>> getAllTasks() {
List<Task> tasks = employeeService.getAllTasks();
Link selfRel = linkTo(methodOn(TaskController.class).getAllTasks()).withSelfRel();
List<Resource<Task>> taskResources = tasks.stream()
.map(TaskController::createTaskResource)
.collect(Collectors.toList());
return new Resources<>(taskResources, selfRel);
}
private static Resource<Task> createTaskResource(Task t) {
Link selfRel = linkTo(methodOn(TaskController.class).getTask(t.getTaskId())).withSelfRel();
return new Resource<>(t, selfRel,
createEmployeeLink(t.getEmployee().getEmployeeId()));
}
static Link createEmployeeLink(long employeeId) {
return linkTo(methodOn(EmployeeController.class).getEmployeeById(employeeId))
.withRel("employee");
}
}
@RestController
public class DeptController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/depts/{deptId}")
public Resource<Dept> getDept(@PathVariable long deptId) {
Dept dept = employeeService.getDeptByDeptId(deptId);
Resource<Dept> deptResource = createDeptResource(dept);
List<Employee> employees = employeeService.getEmployeesByDept(deptId);
employees.stream()
.forEach(e -> deptResource.add(TaskController.createEmployeeLink(e.getEmployeeId())));
deptResource.add(linkTo(methodOn(DeptController.class).getAllDepts()).withRel("depts"));
return deptResource;
}
@GetMapping("/depts")
public Resources<Resource<Dept>> getAllDepts() {
List<Dept> depts = employeeService.getAllDept();
Link selfRel = linkTo(methodOn(DeptController.class).getAllDepts()).withSelfRel();
List<Resource<Dept>> deptResources = depts.stream()
.map(d -> createDeptResource(d))
.collect(Collectors.toList());
return new Resources<>(deptResources, selfRel);
}
private static Resource<Dept> createDeptResource(Dept dept) {
Link selfRel = linkTo(methodOn(DeptController.class).getDept(dept.getDeptId())).withSelfRel();
return new Resource<>(dept, selfRel);
}
}
Running
To try examples, run embedded tomcat (configured in pom.xml of example project below):
mvn tomcat7:run-war
Output
We are going to use
Let's get all employees first:
$ curl -s http://localhost:8080/employees | jq { "_embedded" : { "employeeList" : [ { "employeeId" : 1, "name" : "Lara", "dept" : { "deptId" : 1, "name" : "QA" }, "_links" : { "self" : { "href" : "http://localhost:8080/employees/1" } } }, { "employeeId" : 3, "name" : "Tina", "dept" : { "deptId" : 2, "name" : "IT" }, "_links" : { "self" : { "href" : "http://localhost:8080/employees/3" } } }, { "employeeId" : 2, "name" : "Tom", "dept" : { "deptId" : 1, "name" : "QA" }, "_links" : { "self" : { "href" : "http://localhost:8080/employees/2" } } }, { "employeeId" : 4, "name" : "John", "dept" : { "deptId" : 2, "name" : "IT" }, "_links" : { "self" : { "href" : "http://localhost:8080/employees/4" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:8080/employees" } } }
Employee with id 4:
$ curl -s http://localhost:8080/employees/4 | jq { "employeeId" : 4, "name" : "John", "dept" : { "deptId" : 2, "name" : "IT" }, "_links" : { "self" : { "href" : "http://localhost:8080/employees/4" }, "tasks" : { "href" : "http://localhost:8080/employees/4/tasks" }, "dept" : { "href" : "http://localhost:8080/depts/2" } } }
Now let's get above employee's dept via http://localhost:8080/depts/2
$ curl -s http://localhost:8080/depts/2 | jq { "deptId" : 2, "name" : "IT", "_links" : { "self" : { "href" : "http://localhost:8080/depts/2" }, "employee" : [ { "href" : "http://localhost:8080/employees/3" }, { "href" : "http://localhost:8080/employees/4" } ], "depts" : { "href" : "http://localhost:8080/depts" } } }
All tasks of employee with id = 4:
$ curl -s http://localhost:8080/employees/4/tasks | jq { "_embedded" : { "taskList" : [ { "taskId" : 1, "name" : "Development", "employee" : { "employeeId" : 4, "name" : "John", "dept" : { "deptId" : 2, "name" : "IT" } }, "_links" : { "self" : { "href" : "http://localhost:8080/tasks/1" }, "employee" : { "href" : "http://localhost:8080/employees/4" } } }, { "taskId" : 5, "name" : "Deployment", "employee" : { "employeeId" : 4, "name" : "John", "dept" : { "deptId" : 2, "name" : "IT" } }, "_links" : { "self" : { "href" : "http://localhost:8080/tasks/5" }, "employee" : { "href" : "http://localhost:8080/employees/4" } } } ] } }
$ curl -s http://localhost:8080/tasks/1 | jq { "taskId" : 1, "name" : "Development", "employee" : { "employeeId" : 4, "name" : "John", "dept" : { "deptId" : 2, "name" : "IT" } }, "_links" : { "self" : { "href" : "http://localhost:8080/tasks/1" }, "employee" : { "href" : "http://localhost:8080/employees/4" } } }
All depts:
$ curl -s http://localhost:8080/depts | jq { "_embedded" : { "deptList" : [ { "deptId" : 1, "name" : "QA", "_links" : { "self" : { "href" : "http://localhost:8080/depts/1" } } }, { "deptId" : 2, "name" : "IT", "_links" : { "self" : { "href" : "http://localhost:8080/depts/2" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:8080/depts" } } }
Example ProjectDependencies and Technologies Used: - spring-hateoas 0.24.0.RELEASE:
Library to support implementing representations for
hyper-text driven REST web services.
Uses org.springframework:spring-web-mvc version 4.3.12.RELEASE - spring-plugin-core 1.2.0.RELEASE: Core plugin infrastructure.
- jackson-databind 2.9.5: General data-binding functionality for Jackson: works on core streaming API.
- javax.servlet-api 3.0.1 Java Servlet API
- JDK 10
- Maven 3.5.4
|
|