Pragmatische Küchentricks für RESTful HAL APIs

Die Hypertext Application Language ist als Medientyp-Erweiterung zu Plain JSON/XML nicht neu und kommt in vielen Projekten mit Hypermedia-APIs zum Einsatz.

In Pocket speichern vorlesen Druckansicht 7 Kommentare lesen

(Bild: NicoElNino/Shutterstock.com)

Lesezeit: 14 Min.
Von
  • Leonardo Ramirez
  • Dirk Lingstädt
Inhaltsverzeichnis

Die Hypertext Application Language (HAL) ist eine offene Spezifikation, um die Repräsentationen von RESTful-Ressourcen zu strukturieren. Dazu werden JSON und XML um einen neuen Hypermedia-Typ erweitert. Der initiale Entwurf stammt von Mike Kelly aus dem Jahr 2011 und ist somit schon lange keine Neuheit mehr. Er kommt bereits in vielen API-Projekten zum Einsatz und wird in diversen Artikeln diskutiert. In den letzten Jahren hat das Unternehmen der Autoren eine mittelgroße API entwickelt, die komplexe Daten-Ressourcen in einem weltweiten Unternehmensverbund bereitstellt.

Anstatt für die Entwicklung wie so oft die neueste Technologie zu wählen – und auch inspiriert durch das Richardson Maturity Model – ist das Unternehmen mit HAL als bewährte und ausgereifte Technologie einen pragmatischen Weg gegangen. Im Laufe des Projekts wurden API und Services in mehreren Lebenszyklen entwickelt. Dabei hat das Projektteam für eine auf HAL basierende Architektur viel Praxiserfahrung bei der Konzeption, der Entwicklung, den Betrieb und den Knowledge-Transfer gesammelt.

Wie bei den meisten Technologien, die schon eine Weile existieren, gibt es online zahlreiche Dokumentationen und Tutorials. Aufgrund dessen begnügt sich der Artikel mit den relevanten Grundlagen. Im Kern ist HAL ein Hypermedia-Format, mit dem sich JSON- oder XML-Repräsentationen verknüpfen lassen. Dabei springen zwei Konzepte ins Auge: das teilweise oder vollständige Einbinden anderer Ressourcen in die Ressource selbst und die formale Verlinkung zu anderen Ressourcen.

Jede HAL-Repräsentation enthält demnach eine Map _links, um von der Ressource ausgehend weiter zu navigieren, und eine optionale Map mit _embedded-Ressourcen, die wiederum _links enthalten – beispielsweise den sogenannten self-Link, unter dem die vollständige Ressource zu finden ist. Die API-Konsumenten konnten sich so auf die angebotenen, zustandsbehafteten Links konzentrieren und blieben unabhängig von fest codierten URIs oder internen IDs. Dabei halfen ihnen bewährte Tools (z. B. in Spring HATEOAS) oder es wurde in Teilen selbst entwickelt.

Die Links einer Ressource lassen sich über ihren Relationsnamen zuordnen und semantisch belegen. Dabei greift HAL auf einen etablierten Standard (RFC 8288) zurück – wie self für die aktuelle Ressource oder previous/next für seitenweises Blättern in einer Listen-Ressource. Diese allgemeinen Relationsnamen kommen somit nur für die grundsätzlichen API-Funktionen zum Einsatz. Für benutzerdefinierte Erweiterungen – in der Sprache der Domain – erlaubt die IANA Registry die Verwendung von Uniform Resource Identifier (URI) als Relationsnamen, die gleichzeitig auf die Dokumentation der Relation verweisen sollen. Für eine Kundenverwaltung beispielsweise könnte ein Link zu allen Bestellungen des Kunden hilfreich sein.

{
  "name": "Maxi Mustermann",
  "_links": {
    "self": {
      "href": "http://example.com/customer/123"
    },
    "http://example.com/rels/customer-orders": { // sperriger Relationsname 
      "href": "http://example.com/customer/123/orders"
    }
  }
}

Listing 1: Benutzerdefinierte Relationsnamen, hier als URIs

Das Curie-Konzept (compact URI) hilft dabei, die sperrigen URI-Relationsbezeichner durch einen zweiteiligen Alias für eine parametrisierbare URI zu ersetzen. Im folgenden Beispiel würde die rel=ex:customer-orders auf die Dokumentation in http://example.com/rels/customer-address zeigen:

{
  "name": "Maxi Mustermann",
  "_links": {
    "self": {
      "href": "http://example.com/customer/123"
    },
    "ex:customer-orders": { // griffiger Relationsname
      "href": "http://example.com/customer/123/orders"
    },
    "curies": [
      {
        "href": "http://example.com/rels/{rel}",
        "name": "ex",
        "templated": true
      }
    ]
  },
  "_embedded": { // optional: eingebettete Bestellungen des Kunden
    "ex:customer-orders": [ // gleicher Relationsname wie bei _links
      {
        "orderNumber": "123ASDF",
        "shippingAddress": "Ohlauer Str. 43, 10999 Berlin",
        "_links": {
          "self": {
            "href": "http://example.com/customer/123/orders/ASDF"
          },
          "ex:customer": {
            "href": "http://example.com/customer/123"
          }
        }
      }
    ]
  }
}

Listing 2: Benutzerdefinierte Relationsnamen, hier als Curie-Aliase

Domänenspezifische Relationen lassen sich nicht nur als Link-Bezeichner zum Navigieren der Ressourcen nutzen, sondern auch um andere Ressourcen direkt einbinden (etwa bei Eltern-/Kindbeziehungen). Zum Beispiel könnte die fiktive OrderResource eine CustomerAddressResource einbinden und in einem Response an den Client senden – und so das Extra-GET über den rel=ex:customer-address-Link überflüssig machen.

Begonnen hat das Unternehmen dabei mit statischen Links, dann mit zustandsbehafteten und später – motiviert durch Feedback und tatsächliche Notwendigkeit – hat es die Links verfeinert und die Ressourcen gleich mitgeliefert. Dank des simplen Designs von HAL waren diese Erweiterungen immer transparent und auf Konsumentenseite nur durch die Anzahl der GET-Requests für einen Navigationspfad zu unterscheiden.