HATEOAS

Lesezeit: 7 Minuten

Die Abkürzung HATEOAS steht für Hypermedia as the Engine of Application State und bezeichnet einen Aspekt der REST-Architektur, eine REST-Schnittstelle über Verlinkungen erkundbar zu machen. Ein Client, der eine solche REST-Schnittstelle konsumiert, benötigt idealerweise nur die Root-URL, da er alle Ressourcen der Schnittstelle von dort über Verlinkungen erreicht.

Arten von Verlinkungen

Links an einer Ressource können unterschiedlich motiviert sein:

  • Links, die aufgrund inhaltlicher Nähe immer mit der Ressource ausgeliefert werden
  • Links, die abhängig vom Zustand der Ressource ausgeliefert werden
  • Links, die abhängig vom Benutzer und seinen Rollen ausgeliefert werden

Vorteile für den Konsumenten

Für einen Client als Konsumenten einer REST-Schnittstelle bietet HATEOAS eine Vielzahl an Vorteilen:

  • Keine hart-kodierten URLs im Client, es wird lediglich die Root-URL benötigt
  • Kein manuelles Zusammensetzen von URLs im Client und damit auch keine fehlerhaft zusammengesetzten URLs (fehlende oder doppelte Slashes, nicht URL-enkodierte Zeichenketten, „null“ in der URL…)
  • Weniger Aufwand bei Refactorings, unter Umständen ist gar keine Anpassung im Client notwendig
  • Keine Geschäftslogik im Client – der Client kann aus den Verlinkungen herleiten, welche Elemente ausgeblendet oder deaktiviert werden müssen
  • Reduzierte Komplexität im Client durch alle oben genannten Punkte
  • Für öffentliche APIs ist die Explorierbarkeit der Schnittstelle ein großer Mehrwert, da URLs einfach geändert werden können, wenn die Verlinkungen gleich bleiben

HAL

HATEOAS spezifiziert keine Syntax um eine Ressource und ihre Verlinkungen darzustellen. Ein populärer Standard ist HAL (Hypertext Application Language). Daneben gibt es weitere Spezifikationen wie Collection+JSON, Hydra und Siren.

HAL unterstützt die Formate JSON und XML. In der JSON-Variante werden Links einer Ressource in einer Collection _links mitgeliefert. Ein weiterer interessanter Aspekt der Verlinkung ist die Darstellung von eingebetteten Ressourcen, die in einer Collection _embedded zurückgegeben werden.

{
  "id": "123",
  "name": "HAL-Resource",
  "_links": {
    "self": {
      "href": "https://link-to-self.net"
    },
    "other": {
      "href": "https://link-to-other.net"
    }
  },
  "_embedded": {
    "subresource": {
      "id": "234",
      "name": "HAL-Subresource",
      "_links": {
        "self": {
          "href": "https://link-to-subresource.net"
        }
      }
    }
  }
}

HTTP-Methoden zu den Verlinkungen

Konsumiert man eine REST-Schnittstelle, die HATEOAS umsetzt, stellt sich die Frage, wie man als Konsument die richtige HTTP-Methode identifiziert. Eine Möglichkeit ist es, die HTTP-Methode als Information mit in den Link zu schreiben, im vorherigen HAL-Beispiel etwa neben href als weiteres Feld verb oder method in den Link aufzunehmen. Andererseits kann man argumentieren, dass es die HTTP-Methode OPTIONS gibt, um die erlaubten HTTP-Methoden für einen Endpunkt zu ermitteln. Ich bevorzuge den Weg über den OPTIONS-Request, da ich damit von REST-Bordmitteln Gebrauch mache, anstatt einen Sonderweg zu gehen, der den HATEOAS-gestützten Schnittstellen nach, die ich bisher gesehen habe, eher untypisch erscheint. Die Angabe einer HTTP-Methode zum Link ermöglicht es zwar, der HTTP-Methode im Kontext des Links einen sprechenderen Namen zu geben, andererseits sollte die Intention hinter einem GET, PUT oder DELETE aber eindeutig sein. Lediglich beim POST als Fallback für alles, wo sich keine andere HTTP-Methode besser anbietet, könnte eine zusätzliche Beschreibungsmöglichkeit sinnvoll sein.

Beispiele für den Einsatz von HATEOAS

Verkaufbarer Artikel in einem Webshop

In einem Webshop kann ein Artikel gekauft werden, wenn der Bestand im Lager >= 1 ist. Der Client blendet die Schaltfläche zum Kaufen nur dann ein, wenn der REST-Endpunkt der Artikel-Ressource den Link /kaufen mit ausliefert. Der Client muss nicht wissen, ob der Bestand ausschlaggebend für einen möglichen Kauf ist oder auch noch andere Kriterien eine Rolle spielen. Ändern sich die Bedingungen, muss nur das Backend aktualisiert werden.

Der Artikel 123 in folgendem Beispiel darf gekauft werden. Er darf vom Anwender auch auf die Beobachtungsliste gesetzt und bewertet werden. Möglicherweise darf er nur deswegen bewertet werden, weil der Anwender diesen Artikel in den letzten drei Monaten schon einmal gekauft hat.

{
    "Artikel": {
        "Artikelnummer": "b123",
        "Bestand": 12,
        "Preis": {
            "Waehrung": "EUR",
            "Wert": 50.00
        },
        "links": [{
                "rel": "kaufen",
                "href": "/artikel/b123/kaufen"
            }, {
                "rel": "beobachten",
                "href": "/artikel/b123/beobachten"
            }, {
                "rel": "bewerten",
                "href": "/artikel/b123/bewerten"
        }]
    }
}

Rollenspezifische Aktionsmöglichkeiten

Bestellungen innerhalb eines Unternehmens müssen grundsätzlich von einem Sachbearbeiter freigegeben werden, bevor sie an den Einkauf weitergeleitet werden. Dieser Sachbearbeiter darf die Bestellung genehmigen oder ablehnen, er ist aber beispielsweise nicht berechtigt, die Bestellung direkt zu löschen. Dies darf nur der Antragsteller selbst oder ein Administrator.

{
    "Bestellung": {
        "Bestellnummer": "3498539"
        "Artikelnummer": "b123",
        "Adresse": {
            "Name": "ABC GmbH",            
            "Straße": "Dorfweg 12c",
            "Ort": "Kleinstadt"
        },
        "links": [{
                "rel": "genehmigen",
                "href": "/bestellung/3498539/genehmigen"
            }, {
                "rel": "ablehnen",
                "href": "/bestellung/3498539/ablehnen"
        }]
    }
}

Abonnierte Module einer Software as a Service

Ein Geschäftskunde hat sich für seine Warenwirtschaft ein Software-as-a-Service-Abonnement einer modularisierten Lösung in der Cloud zugelegt. Er hat die Module Artikelstamm, Auftrag und Wareneingang abonniert. Auf das Modul Gutscheinverwaltung verzichtet er allerdings. Das Webfrontend dieser Cloud-Lösung lädt beim Start die verfügbaren Module anhand der angebotenen Links der Root-Ressource im Backend. Der Aktionsumfang innerhalb der Module ist auf die vom Geschäftskunden im Rahmen seines Abonnements gewählten kostenpflichtigen Optionen zugeschnitten. Der Client stellt nur die Aktionen dar, für die das Backend die Links ausliefert.

{
    "links": [{
            "rel": "self",
            "href: "https://example.rest/wws-cloud/"
        }, {
            "rel": "artikelstamm",
            "href: "https://example.rest/wws-cloud/artikelstamm"
        }, {
            "rel": "auftrag",
            "href: "https://example.rest/wws-cloud/auftrag"
        }, {
            "rel": "wareneingang",
            "href: "https://example.rest/wws-cloud/wareneingang"
    }]
}

Fazit

Die drei Beispiele beschreiben Umstände auf verschiedensten Ebenen, die dafür verantwortlich sein können, ob eine Aktion ausgeliefert wird oder nicht:

  • Im ersten Beispiel ist die Verfügbarkeit eines Artikels ausschlaggebend dafür, dass er gekauft werden darf.
  • Im zweiten Beispiel gibt die Rolle dem Anwender vor, dass er eine Bestellung genehmigen oder ablehnen, aber nicht löschen darf.
  • Im dritten Beispiel ist die konkrete Ausprägung des Abonnements eines Nutzers in einem Multimandantensystem entscheidend dafür, welche Module er in welchem Funktionsumfang benutzen darf.
- Internet Draft zu JSON HAL (2016)
- Collection+JSON
- Hydra
- Siren