HATEOAS Acknowledged Dervergessenen Ruhm von REST
Takashi Yamamoto
Infrastructure Engineer · Leapcell

Einleitung
In der sich ständig weiterentwickelnden Landschaft der Backend-Entwicklung waren RESTful APIs seit langem der De-facto-Standard für den Aufbau skalierbarer und wartbarer Webservices. Wenn jedoch Anwendungen komplexer werden, wird ein subtiler, aber tiefgreifender Aspekt von REST oft übersehen oder sogar explizit verworfen: HATEOAS (Hypermedia As The Engine Of Application State). Diese entscheidende Einschränkung, wohl das mächtigste und am wenigsten übernommene Prinzip von REST, verspricht, ein wirklich entkoppeltes und selbsterklärendes API-Erlebnis zu erschließen. Aber in einer Ära, die von OpenAPI-Spezifikationen und clientgesteuerter Entwicklung dominiert wird, ist HATEOAS immer noch eine relevante architektonische Wahl, oder ist es nur zu einem theoretischen Ideal geworden, einem vergessenen Ruhm der ursprünglichen REST-Vision? Dieser Artikel zielt darauf ab, das Wesen von HATEOAS, seine praktischen Auswirkungen und seine potenzielle Wiederbelebung im modernen API-Design zu untersuchen und letztendlich zu fragen, ob seine Zeit gekommen ist oder ob es für immer seiner Zeit voraus bleibt.
HATEOAS Der vergessene Ruhm von REST
Um die Debatte um HATEOAS vollständig zu verstehen, wollen wir zunächst einige grundlegende Konzepte klären.
REST (Representational State Transfer): Ein Architekturstil für vernetzte Hypermedia-Anwendungen. Er definiert eine Reihe von Einschränkungen, die bei Anwendung zu einem verteilten System mit wünschenswerten Eigenschaften wie Leistung, Skalierbarkeit, Einfachheit, Modifizierbarkeit, Sichtbarkeit, Portabilität und Zuverlässigkeit führen. Zu den wichtigsten Einschränkungen gehören Client-Server, Zustrandlosigkeit, Cachebarkeit, geschichtetes System und ein einheitliches Interface.
Einheitliches Interface: Dies ist die wichtigste REST-Einschränkung für unsere Diskussion. Sie schreibt vor, dass es eine einheitliche und generische Möglichkeit für Clients geben sollte, mit Ressourcen zu interagieren. Vier Untereinschränkungen definieren dies weiter:
- Identifizierung von Ressourcen: Ressourcen werden durch URIs identifiziert.
- Manipulation von Ressourcen durch Repräsentationen: Clients manipulieren Ressourcen durch den Austausch von Repräsentationen.
- Selbsterklärende Nachrichten: Jede zwischen Client und Server ausgetauschte Nachricht enthält genügend Informationen, um zu beschreiben, wie sie zu verarbeiten ist.
- Hypermedia als Motor des Anwendungszustands (HATEOAS): Der Server wechselt den Anwendungszustand durch Hypermedia und stellt Links innerhalb von Repräsentationen bereit, die den Client über verfügbare Aktionen und Zustandsübergänge informieren.
HATEOAS erklärt
HATEOAS schreibt vor, dass ein Client keine Vorkenntnisse über die Interaktion mit einer API haben muss, abgesehen von einem anfänglichen Einstiegspunkt. Stattdessen stellt der Server Links innerhalb seiner Antworten zur Verfügung, die den Client darüber informieren, welche Aktionen derzeit verfügbar sind und wie sie ausgeführt werden können. Diese Links fungieren als Steuerelemente, die es der API ermöglichen, sich weiterzuentwickeln, ohne Clients zu beeinträchtigen, solange die Ressourcenrepräsentationen konsistent bleiben.
Betrachten Sie ein Beispiel für eine E-Commerce-Bestellung:
Traditionelle REST-Antwort (ohne HATEOAS):
{ "orderId": "12345", "status": "pending", "totalAmount": 99.99, "customerId": "C001" }
Um diese Bestellung zu aktualisieren, müsste der Client wissen, dass ein PUT /orders/{orderId}-Endpunkt existiert und eine bestimmte Nutzlast erfordert. Zur Stornierung könnte es DELETE /orders/{orderId} kennen. Diese Kopplung bedeutet, dass der Client-Code fehlschlägt, wenn die API ihre Endpunktstruktur oder verfügbaren Aktionen ändert (z. B. können "ausstehende" Bestellungen nach einer bestimmten Zeit nur noch "storniert" und nicht mehr "aktualisiert" werden).
HATEOAS-gesteuerte REST-Antwort:
{ "orderId": "12345", "status": "pending", "totalAmount": 99.99, "customerId": "C001", "_links": { "self": { "href": "/orders/12345", "method": "GET" }, "update": { "href": "/orders/12345", "method": "PUT", "type": "application/json" }, "cancel": { "href": "/orders/12345/cancel", "method": "POST" }, "customer": { "href": "/customers/C001", "method": "GET" } } }
In diesem HATEOAS-Beispiel teilt das _links-Objekt dem Client explizit mit, welche Aktionen im aktuellen Zustand der Bestellung möglich sind. Wenn sich der Bestellstatus in "versendet" ändert, könnte der Server Links zu "sendungsverfolgen" oder "zurückgeben"-Aktionen bereitstellen, aber nicht mehr zu "aktualisieren" oder "stornieren". Der Client muss lediglich die Link-Beziehungen (z. B. "aktualisieren", "stornieren") verstehen und den bereitgestellten URIs und Methoden folgen.
Implementierung in Frameworks
Viele moderne Backend-Frameworks bieten Bibliotheken oder integrierten Support für HATEOAS.
Java (Spring HATEOAS):
Spring HATEOAS ist eine beliebte Bibliothek, die die HATEOAS-Implementierung für Spring Boot-Anwendungen vereinfacht.
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/orders") public class OrderController { @GetMapping("/{id}") public ResponseEntity<EntityModel<Order>> getOrder(@PathVariable String id) { Order order = findOrderById(id); // Annahme, dass dies die Bestellung aus einem Dienst abruft EntityModel<Order> orderModel = EntityModel.of(order); orderModel.add(linkTo(methodOn(OrderController.class).getOrder(id)).withSelfRel()); if ("pending".equals(order.getStatus())) { orderModel.add(linkTo(methodOn(OrderController.class).updateOrder(id, null)).withRel("update")); orderModel.add(linkTo(methodOn(OrderController.class).cancelOrder(id)).withRel("cancel")); } else if ("shipped".equals(order.getStatus())) { orderModel.add(linkTo(methodOn(OrderController.class).trackOrder(id)).withRel("track")); } orderModel.add(linkTo(methodOn(CustomerController.class).getCustomer(order.getCustomerId())).withRel("customer")); return ResponseEntity.ok(orderModel); } // Andere Methoden wie updateOrder, cancelOrder, trackOrder mit @PostMapping oder @PutMapping // ... }
In diesem Spring-Beispiel kapselt EntityModel.of(order) das Order-Objekt, und dann wird add() verwendet, um Link-Objekte anzuhängen, die zur Bequemlichkeit mithilfe von WebMvcLinkBuilder erstellt werden. linkTo und methodOn sind leistungsstarke Werkzeuge zum Generieren von URIs basierend auf Controller-Methoden.
Node.js (Express mit einer HATEOAS-Hilfsbibliothek):
Obwohl Node.js keine direkte Entsprechung zu Spring HATEOAS von Haus aus hat, können Hilfsbibliotheken erstellt oder übernommen werden.
// order.routes.js const express = require('express'); const router = express.Router(); function addHateoasLinks(order) { const links = { self: { href: `/orders/${order.orderId}`, method: 'GET' }, customer: { href: `/customers/${order.customerId}`, method: 'GET' } }; if (order.status === 'pending') { links.update = { href: `/orders/${order.orderId}`, method: 'PUT', type: 'application/json' }; links.cancel = { href: `/orders/${order.orderId}/cancel`, method: 'POST' }; } else if (order.status === 'shipped') { links.track = { href: `/orders/${order.orderId}/track`, method: 'GET' }; } return { ...order, _links: links }; } router.get('/:id', (req, res) => { const orderId = req.params.id; const order = findOrderById(orderId); // Annahme, dass dies die Bestellung abruft if (!order) { return res.status(404).send('Order not found'); } res.json(addHateoasLinks(order)); }); module.exports = router;
Dieses Node.js-Beispiel zeigt einen manuellen Ansatz zum Einfügen von HATEOAS-Links, der oft in einer Dienstprogrammfunktion oder einem Middleware gekapselt ist.
Anwendungsszenarien
HATEOAS glänzt in bestimmten Szenarien:
- API-Evolution und langfristige Wartbarkeit: Wenn die Funktionalität einer API häufig geändert werden soll oder neue Funktionen hinzugefügt werden, ermöglicht HATEOAS Clients die Anpassung, ohne dass für jede Endpunktänderung Codeänderungen erforderlich sind.
- Generische Clients: Die Erstellung wirklich generischer Clients, die mit jeder API interagieren können, die einen bestimmten Ressourcentyp bereitstellt, indem sie einfach Hypermedia-Steuerelementen folgen, ohne spezifische Endpunktkenntnisse.
- Menschenlesbare APIs: Obwohl Clients normalerweise Programme sind, kann die selbsterklärende Natur von HATEOAS es auch für menschliche Entwickler einfacher machen, eine API zu erkunden und zu verstehen.
- Entkopplung von Client und Server: Reduzierung der engen Kopplung zwischen Client und Server, was eine unabhängige Bereitstellung und Versionierung jedes einzelnen ermöglicht.
Warum ist es "vergessen"?
Trotz seiner architektonischen Vorteile steht HATEOAS vor Herausforderungen:
- Komplexität für einfache APIs: Für einfache CRUD-APIs kann der Overhead für die Generierung und das Parsen von Links unnötig erscheinen.
- Clientseitige "Ignoranz": Viele Clients sind darauf ausgelegt, genau zu wissen, welche Endpunkte sie aufrufen sollen. Entwickler bevorzugen oft eine explizite API-Dokumentation (wie OpenAPI) gegenüber der dynamischen Link-Erkennung.
- Mangelnde Werkzeuge: Obwohl einige Frameworks Unterstützung bieten, sind robuste, sprachunabhängige HATEOAS-Parser und SDK-Generatoren für Clients seltener als die für traditionelles REST.
- Verwechslung mit Zuständigkeit: Missverständnis der Beziehung zwischen HATEOAS und Zuständigkeit. HATEOAS verwaltet Anwendungszustandsübergänge über Hypermedia, nicht serverseitigen Sitzungszustand.
- Entwicklerdenkweise: Das vorherrschende Entwicklungsparadigma bevorzugt oft explizite Verträge und frühe Bindung, was HATEOAS in Frage stellt.
Fazit
HATEOAS, obwohl ein Eckpfeiler des REST-Architekturstils, ist in der Mainstream-API-Gestaltung weitgehend ein nicht umgesetztes Ideal geblieben. Es bietet unbestreitbare Vorteile in Bezug auf API-Erkennbarkeit, Evolvierbarkeit und echte Client-Server-Entkopplung, was es zu einem leistungsstarken Werkzeug für langlebige, komplexe Systeme macht, bei denen unabhängige Evolution von größter Bedeutung ist. Seine wahrgenommene Komplexität, die Verbreitung alternativer Dokumentationen wie OpenAPI und eine allgemeine Präferenz der Entwickler für explizite Verträge haben jedoch seine Einführung marginalisiert. Obwohl nicht jede API die volle Leistung von HATEOAS benötigt, kann die Umarmung von HATEOAS für diejenigen, die wirklich robuste und anpassungsfähige Architekturen suchen, eine API von einem statischen Vertrag in eine dynamische, selbst navigierbare Landschaft verwandeln und beweisen, dass sie ein potentes und relevantes, wenn auch oft übersehenes Prinzip in der modernen API-Gestaltung bleibt.

