spring-hateoas

Spring hateoas


Evolution

rest_evolution

Hypermedia as the Engine of Application State


RPC

  • Client muss für jede Ressource URI wissen
  • keine Contextabhängigkeiten
  • statisch

HAL

Hypertext Application Language hal_model


{
  "id": 4,
  "name": "HTL Student",
  "number": 100,
  "school": {
    "id": 3,
    "name": "HTL"
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/api/students/4"
    },
    "students": {
      "href": "http://localhost:8080/api/students"
    },
    "school": {
      "href": "http://localhost:8080/api/schools/3"
    }
  }
}

HATEOAS

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>

GET

@GetMapping("/students/{id}")
Student one(@PathVariable Integer id) {
  return repository.findById(id)
      .orElseThrow(() -> new StudentNotFoundException(id));
}
@GetMapping("/students/{id}")
EntityModel<Student> one(@PathVariable Integer id) {
  var student = repository.findById(id)
      .orElseThrow(() -> new StudentNotFoundException(id));
  return EntityModel.of(student,
      linkTo(methodOn(StudentHateosRestController.class).one(id))
          .withSelfRel(),
      linkTo(methodOn(StudentHateosRestController.class).all())
          .withRel("students"));
}

@GetMapping("/students/{id}")
public EntityModel<Student> one(@PathVariable Integer id) {
  var student = repository.findById(id)
      .orElseThrow(() -> new StudentNotFoundException(id));
  return assembler.toModel(student);
}
@Component
public class StudentModelAssembler implements 
      RepresentationModelAssembler<Student, EntityModel<Student>> {

  @Override
  public EntityModel<Student> toModel(Student student) {
    return EntityModel.of(student,
        linkTo(methodOn(StudentHateoasRestController.class)
           .one(student.getId())).withSelfRel(),
        linkTo(methodOn(StudentHateoasRestController.class)
           .all()).withRel("students"));
  }
}

POST

@PostMapping("/students")
ResponseEntity<EntityModel<Student>> newStudent(
      @Valid @RequestBody Student student) {

  Student saved;
  saved = trySave(student);
  var studentModel = assembler.toModel(saved);
  return ResponseEntity
       .created(studentModel
           .getRequiredLink(IanaLinkRelations.SELF).toUri())
       .body(studentModel);
}