REST heißt HATEOAS

Nur wer sein Ziel kennt, findet den Weg.

Laozi

Häufig existieren REST Schnittstellen, die Resourcen im Json Formate zurückliefern, die keinerlei Verknüpfung zu anderen Resourcen besitzen. Schlimmer noch, sie offenbaren interne Werte, damit der Client die Möglichkeit hat, andere Resourcen zu adressieren oder zu manipulieren. Häufig ist es dies der Schlüssel der Resourcen in der Datenbank.

{
  "id": 42,
  "address": 23,
  "name": "Sirius Cybernetics Corporation",
}

Das obige Beispiel zeigt den Rückgabewert einer REST Schnittstelle für Firmendaten. Enthalten ist der Name der Firma sowie der Schlüssel für diesen Eintrag in der Datenbank und zusätzlich ein Schlüssel für eine Adressen Resource. Manchmal werden auch alle Informationen am Stück herumgereicht, damit sich jeder das heraussuchen kann, was er gerade benötigt.

{
  "id": 42,
  "address": {
    "id": 23,
    "street2: "",
    "city": "",
    "zip": ""
  }
  "name": "Sirius Cybernetics Corporation"
}

Hier sehen wir einige Dinge, die nicht wirklich gefallen. Zum einen muss der Client wissen, wie er aus der ID der Adresse einen Link erstellt, um die zusätzlichen Adressinformationen zu erhalten. Zum anderen benötigt er relativ viel Wissen über die Datenstrukturen, die von der REST Schnittstelle zur Verfügung gestellt werden.

Ein weiterer hässlicher Aspekt bei der Verwendung von Datenbankschlüsseln in DTO Klassen sind PUT Request zum Manipulieren einer Resource. Ein Kollege möchte eine Adresse ändern und schickt daher einen PUT Request an die REST Schnittstelle.

PUT http://acme.com/companies/api/latest/address/42
{
  "id": 24,
  "street": "Riverside Drive", 
  "number": "1640 "
  "city": "Hill Valley", 
  "state": "California"
}

Ärgerlicherweise gab es einen Zahlendreher bei der ID und nun ist nicht wirklich klar, welcher Eintrag geändert wird. Ist es nun die Adresse der Sirius Cybernetics Corporation oder wird auf die ID im Request Body geachtet und wir korrigieren tatsächlich die Adresse von Dr. Brown?

Hier kommt es tatsächlich auf die Implementierung an, welche ID tatsächlich verwendet wird. Wäre keine ID im Request und somit im DTO möglich, so hätten wir eine Fehlerquelle weniger.

Mit HATEOAS gestaltet sich der Umgang mit einer REST Schnittstelle um vieles einfacher. HATEOAS steht für Hypermedia As The Engine Of Application State und ist ein ganz zentrales Prinzip für REST Schnittstellen. Mögliche Übergänge zwischen verschiedenen Zuständen oder Wechsel zwischen Resourcen werden von der Rest Schnittstelle als Hyperlinks zur Verfügung gestellt.

Die obige Antwort der REST Schnittstelle könnte beispielsweise wie folgt aussehen.

{ 
  "company": {
    "name": "Sirius Cybernetics Corporation"
  },
  "_links": {
    "self": { "href": "http://acme.com/companies/api/latest/company/42" },
    "address": { "href": "http://soylent:8080/companies/api/1/company/42/address" }
  }
}

Damit der Client auf die unterschiedlichen Informationen zugreifen oder sie manipulieren kann, liefert die REST Schnittstelle einige Links mit. Der Client muss also nur wissen, wie er einen Link vom Typ address zu interpretieren hat, er muss keine Informationen darüber besitzen, welche API Version der Rest Schnittstelle er aufrufen dar, oder welcher Server angesprochen werden sollte. In diesem Fall den Server der Soylent Corporation, der nur die API Version 1 unterstützt. Auch ist die ID der Adresse verschwunden, weil die Adresse über die Firma abgerufen wird. Wie auch immer dies implementiert ist.

Wenn möglich, kann über den Link die Resource auch per PUT Request verändert oder per DELETE Request gelöscht werden. Sollen Informationen zu Mitarbeitern, Abteilungen oder Standorten der Firma hinzugefügt werden, dann ergänzt man diese im Link Bereich.

{
  "company": {
    "name": "Sirius Cybernetics Corporation"
  },
  "_links": {
    "self": { "href": "http://acme.com/companies/api/latest/company/42" },
    "address": { "href": "http://soylent:8080/companies/api/1/company/42/address" }
    "facilities": { "href": "http://soylent:8080/companies/api/1/company/42/facilities" }
    "employees": { "href": "http://acme.com/companies/api/latest/company/42/employees" },
  }
}

Wie einfach die HATEOAS Unterstützung mit Spring Boot HATEOAS implementiert werden kann, wurde schon kurz im Beitrag REST in Peace angeschnitten.

Im Grunde müssen nur die Rückgabeobjekte die Klasse ResourceSupport erweitern oder in einer Implementierung von ihr eingebettet werden. Im obigen Beispiel wurde die Klasse CompanyDto in CompanyResource eingebettet. Auf diese Weise ist der HATEOAS Teil in CompanyResource gekapselt und der fachliche Teil verbleibt in CompanyDto.