Gleich vorneweg: Ich liebe REST-Schnittstellen. Eine wohl definierte API zu schaffen ist schon fast ein künstlerischer Schöpfungsprozess. Die Grundlagen (HTTP) sind jedem bekannt, und trotzdem hat man viele Freiheiten. Umso mehr kann man alles falsch oder eben auch vieles richtig machen. Trifft letzteres zu, erschliesst sich das Resultat dem Betrachter sofort ohne viele Erklärungen. Eben wie ein grosses Kunstwerk.
Und NextCarWash, meine Suche für die nächste Autowaschanlage, bietet eine solche Schnittstelle. Die ganze Kommunikation mit dem Webfrontend geschieht über diese API, entsprechend gut ist sie in der Praxis erprobt und mehr als ein theoretisches Konstrukt.
Die Schnittstelle setzt das HATEOAS-Prinzip um. Dieses definiert, dass über Links die verschiedenen Ressourcen miteinander verknüpft sind. Dies entspricht dem Ansatz, dass Internetseiten über Hyperlinks aufeinander verweisen. Damit entspricht sie dem Level 3 des von Leonard Richardson entwickelten REST Maturity Model. Die Daten werden im JSON-Format übermittelt, deren Struktur durch die Hypertext Application Language (HAL) definiert wird.
Nun genug der Theorie, schauen wir uns ein konkretes Beispiel an. Wir holen uns eine bestimmten Anlage:
http://www.nextcarwash.ch/api/rest/carwash/CW93X5UT
Der Link beinhaltet die eindeutige ID der gewünschten Anlage. Als Antwort erhalten wir folgende JSON:
{ "publicId": "CW93X5UT", "location": { "longitude": 7.458025, "latitude": 46.9235649 }, "operator": "Migrol Service", "manufacturer": "Migrol", "openinghours": "Mo - Fr 8.00 - 18.30\nSa 7.30 - 17.00\nSo geschlossen\n", "address": "Chly Wabern\nSeftigenstrasse 350\n3084 Wabern\n", "website": "http://www.migrol.ch/de/Car-Wash.aspx", "imported": true, "_links": { "tags": { "href": "/api/rest/carwash/CW93X5UT/tags", "title": "all tags of this carwash" }, "self": { "href": "/api/rest/carwash/CW93X5UT" }, "comments": { "href": "/api/rest/carwash/CW93X5UT/comments", "title": "all comments of this carwash" } }, "_embedded": { "tags": [ { "publicId": "TGO5BRW8", "name": "Waschstrasse", "_links": { "self": { "href": "/api/rest/tag/TGO5BRW8" } } }, { "publicId": "TGHP82WX", "name": "Self-service Anlage", "_links": { "self": { "href": "/api/rest/tag/TGHP82WX" } } }, { "publicId": "TGPHNEEM", "name": "Zahlen mit Kreditkarte", "_links": { "self": { "href": "/api/rest/tag/TGPHNEEM" } } }, { "publicId": "TGUUKZYU", "name": "Bedient", "_links": { "self": { "href": "/api/rest/tag/TGUUKZYU" } } }, { "publicId": "TGIACBC9", "name": "Shop", "_links": { "self": { "href": "/api/rest/tag/TGIACBC9" } } } ] } }
Hier sieht man nun sehr gut das umgesetzte HATEOAS-Prinzip. Zeile 19 enthält den Link auf sich selbst. In Zeile 15 geht es zu allen Eigenschaften dieser Anlage. Der Link in Zeile 20 führt uns zu den Kommentaren. So kann man problemlos zwischen den verschiedenen Ressourcen navigieren.
Ab Zeile 26 folgenden die zugewiesenen Eigenschaften. Diese werden direkt mitgeliefert, um nicht nachträglich nachgeladen zu werden. Jede dieser Eigenschaften hat wieder einen self-Link, welche auf die eigentliche Ressource verweist.
Der HAL-Browser nutzt dieses standardisierte Format und bietet eine komfortable Möglichkeit, die Schnittstelle zu durchforsten.
Neben GET unterstützt die API natürlich auch die anderen Verben. Mit POST kann man Anlagen, Eigenschaften und Kommentare hinzufügen. Ersteres kann man mit PUSH aktualisieren bzw. mit DELETE wieder löschen. Dazu gibt es noch eine Suche über einen geografischen Bereich. Bitte beachtet die Dokumentation der Schnittstelle für weitere Informationen.
Umgesetzt ist die Schnittstelle mit JAX-RS und halarious. halarious ist ein auf GSON basierendes Framework, um HAL-konformes JSON einfach zu erstellen. Die einzelnen Ressourcen werden als normale Java-Klasse geschrieben, mit halarious-Annotation ergänzt und mit GSON serialisiert. Konkret steht hinter obigem JSON folgende Klasse:
public class CarwashHalResource extends HalBaseResource { @HalLink(name = "tags", title = "all tags of this carwash") private String tagLink; @HalLink(name = "comments", title = "all comments of this carwash") private String commentLink; private String publicId; private String name; private Point location; private String operator; private String manufacturer; private String openinghours; private String address; private String remarks; private String website; private Boolean imported; @HalEmbedded private final List<TagHalResource> tags = new ArrayList<>(); /** Konstruktor, Getter, Setter */ }
Das ganze HAL-Model steht als eigenes Maven-Artefakt zur Verfügung. Dies sollte den Aufruf der Schnittstelle zum Beispiel in einer Android-App (oder allem anderen, was mit Java umgesetzt ist) deutlich vereinfachen.
Zum Abschluss nochmals die wichtigsten Links:
Und nun noch ein Aufruf: Wenn jemand Lust hat, basierend auf dieser API eine Android- oder iOS-App zu schreiben, würde mich das sehr freuen! Für Fragen und Unterstützung stehe ich sehr gerne zur Verfügung.