10 HATEOAS

10.1 Einführung

HATEOAS (Hypermedia as the Engine of Application State) ist ein zentrales Prinzip des REST-Architekturstils. Es besagt, dass ein Client die Navigation durch die Anwendung mithilfe von Hypermedia steuern soll, die vom Server in den Antworten bereitgestellt wird. Dies ermöglicht es, dass der Client keine Kenntnisse über die interne Struktur oder die verfügbaren Endpunkte des Servers haben muss. Stattdessen entdeckt er diese dynamisch über die erhaltenen Hypermedia-Links.

10.2 Grundprinzipien von HATEOAS

  1. Hypermedia als Navigationsmittel

    Der Server liefert zusammen mit den Daten Links, die dem Client erlauben, weitere Aktionen auszuführen oder zu verwandten Ressourcen zu navigieren.

  2. Entkopplung von Client und Server

    Änderungen an der API-Struktur oder den Endpunkten auf Serverseite beeinträchtigen den Client nicht, da er die verfügbaren Aktionen dynamisch aus den erhaltenen Links ableitet.

  3. Selbstbeschreibende Nachrichten

    Jede Antwort enthält genügend Informationen, damit der Client den aktuellen Zustand versteht und mögliche nächste Schritte kennt.

10.3 Vorteile von HATEOAS

10.4 Implementierung von HATEOAS in Spring Boot

Spring Boot bietet mit dem Projekt Spring HATEOAS Unterstützung für die Erstellung von Hypermedia-getriebenen RESTful Services.

10.4.1 Schritt 1: Abhängigkeit hinzufügen

Fügen Sie in Ihrer pom.xml oder build.gradle die folgende Abhängigkeit hinzu:

10.4.2 Maven

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

10.4.3 Gradle

implementation 'org.springframework.boot:spring-boot-starter-hateoas'

Verwenden Sie EntityModel oder CollectionModel, um Ressourcen mit Links zu versehen.

10.4.5 Beispiel: Implementierung eines einfachen HATEOAS-Controllers

Angenommen, wir haben ein Employee-Objekt und möchten einen RESTful Service erstellen.

10.4.5.1 Employee.java

public class Employee {
    private Long id;
    private String name;
    private String role;

    // Konstruktoren, Getter und Setter
}

10.4.5.2 EmployeeController.java

import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@RequestMapping("/employees")
public class EmployeeController {

    private Map<Long, Employee> employeeRepository = new HashMap<>();

    @GetMapping("/{id}")
    public EntityModel<Employee> getEmployee(@PathVariable Long id) {
        Employee employee = employeeRepository.get(id);

        if (employee == null) {
            throw new EmployeeNotFoundException(id);
        }

        EntityModel<Employee> employeeModel = EntityModel.of(employee,
                WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(EmployeeController.class).getEmployee(id)).withSelfRel(),
                WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(EmployeeController.class).getAllEmployees()).withRel("employees"));

        return employeeModel;
    }

    @GetMapping
    public CollectionModel<EntityModel<Employee>> getAllEmployees() {
        List<EntityModel<Employee>> employees = employeeRepository.values().stream()
                .map(employee -> EntityModel.of(employee,
                        WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(EmployeeController.class).getEmployee(employee.getId())).withSelfRel()))
                .collect(Collectors.toList());

        return CollectionModel.of(employees,
                WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(EmployeeController.class).getAllEmployees()).withSelfRel());
    }

    // Weitere Methoden wie createEmployee, updateEmployee etc.
}

10.4.6 Erläuterung

10.4.7 Schritt 3: Exception Handling

Erstellen Sie eine benutzerdefinierte Ausnahme für den Fall, dass ein Mitarbeiter nicht gefunden wird.

10.4.7.1 EmployeeNotFoundException.java

@ResponseStatus(HttpStatus.NOT_FOUND)
public class EmployeeNotFoundException extends RuntimeException {
    public EmployeeNotFoundException(Long id) {
        super("Mitarbeiter mit ID " + id + " nicht gefunden.");
    }
}

10.5 Beispiel einer Serverantwort mit HATEOAS

10.5.1 Anfrage

GET /employees/1

10.5.2 Antwort

{
  "id": 1,
  "name": "Max Mustermann",
  "role": "Entwickler",
  "_links": {
    "self": {
      "href": "http://localhost:8080/employees/1"
    },
    "employees": {
      "href": "http://localhost:8080/employees"
    }
  }
}

Der Client kann die erhaltenen Links verwenden, um weitere Aktionen durchzuführen, z. B.:

10.6.1 Diagramm: Client-Server-Interaktion mit HATEOAS

10.7 Best Practices

10.8 Einschränkungen von HATEOAS

10.9 HATEOAS und API-Versionierung

Durch die Verwendung von HATEOAS kann die Notwendigkeit expliziter Versionsangaben in den URIs reduziert werden, da der Client die verfügbaren Aktionen und Ressourcen über die Links entdeckt.

10.10 Integration mit anderen Technologien

Beispiel: Verwendung von HAL in der Antwort

{
  "_embedded": {
    "employees": [
      {
        "id": 1,
        "name": "Max Mustermann",
        "role": "Entwickler",
        "_links": {
          "self": {
            "href": "http://localhost:8080/employees/1"
          }
        }
      },
      {
        "id": 2,
        "name": "Erika Musterfrau",
        "role": "Managerin",
        "_links": {
          "self": {
            "href": "http://localhost:8080/employees/2"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/employees"
    }
  }
}

10.11 Zusammenfassung der Vorteile von HATEOAS

10.12 tl;dr

HATEOAS ist ein REST-Prinzip, das die Navigation eines Clients durch Hypermedia-Links ermöglicht, die in Serverantworten eingebettet sind. Dies fördert eine lose Kopplung zwischen Client und Server und ermöglicht flexible und erweiterbare APIs. Mit Spring Boot und Spring HATEOAS können Entwickler effizient Hypermedia-getriebene RESTful Services erstellen.