„To REST or not to REST“

Enterprise Tales: Pragmatic REST

Lars Röwekamp

„To REST or not to REST“, das ist hier die Frage. Eines ist sicher, nur weil ich JSON oder XML Payload via HTTP an einen Server sende oder von eben diesem empfange, habe ich es sicherlich noch nicht zwangsweise mit einem RESTful API zu tun. Was also genau macht eine REST-Schnittstelle aus? Und ab wann kann man sie als wirklich gut gelungen bezeichnen? Spätestens bei dieser Frage gehen die Meinungen stark auseinander.

Bei Diskussionen rund um das Thema REST kommt irgendwann unweigerlich die Frage auf „Ja, ist denn das dann überhaupt noch REST?“. So auch auf dem unlängst in Berlin stattgefundenen Microservices Summit. Die Meinungen, was bei REST erlaubt ist und was nicht, gingen dabei zum Teil weit auseinander. Warum kommt es eigentlich immer wieder zu derartigen Diskussionen rund um den korrekten Einsatz von Rest? Und ist dies für das Design eines anwenderfreundlichen API überhaupt von Relevanz?

REST at its best
Wahrscheinlich haben die wenigsten Entwickler von RESTful APIs jemals einen tieferen Blick in die Dissertation von Roy Thomas Fielding [1] aus dem Jahre 2000 geworfen. Fielding beschreibt dort in Kapitel 5 einen neuen Ansatz für netzwerkbasierte Softwarearchitekturen namens REST (Representational State Transfer). Das Interessante daran ist, dass wir in der Dissertation recht wenig über Endpoints, HTTP-Methoden oder gar JSON beziehungsweise XML lesen. Es geht in der Arbeit vielmehr um einen Architekturansatz, der durch Begriffe wie Clientserver, Stateless, Chaching, Uniform Interfaces, Layered System und Code on Demand geprägt ist. Das, was die meisten von uns unter REST verstehen, nämlich die Identifikation einer Ressource mittels eindeutiger URL sowie deren Manipulation durch eine Repräsentation der Ressource (JSON oder XML) im Zusammenspiel mit selbsterklärenden Messages (aka HTTP-Methoden), spielt in der Arbeit eher eine untergeordnete Rolle und wird dort mit „the four interface constraints“ bezeichnet. „four?“ wird sich jetzt der eine oder andere aufmerksame Leser fragen. Aber ich lese hier doch nur drei! Richtig. Und genau hier liegt der Knackpunt. Als viertes Constraint führt Fielding „Hypermedia as the engine of application state“ auf. Die beiden folgenden Zitate von Fielding zeigen, welchen Stellenwert er persönlich den Begriffen Hypermedia und Hypertext im Kontext von REST beimisst:

  1. „If the engine of application state (and hence the API) is not driven by hypertext, then it cannot be RESTful and cannot be a REST API. “
  2. „A REST API should be entered with no prior knowledge beyond the initial URI. From that point on, all application state transitions must be driven by the client selection of server-provides choices.“

Fielding sagt also, dass ein RESTful API ohne vorherige Kenntnisse jenseits des initialen URI auskommen sollte. Der Aufruf dieses URI liefert eine Liste von Links (als Teil der Payload oder alternativ als Link-Header) mit möglichen und sinnvollen Operationen. Für dieses Modell gibt es natürlich auch ein entsprechendes, mehr oder minder sinnvolles Akronym: HATEOAS (Hypermedia as the Engine of State Transfer).

HATEOAS in Aktion
Zugegeben, dass eben geschilderte Szenario klinkt für viele wahrscheinlich erst einmal ein wenig abstrakt. Dabei ist es genau das, was uns täglich im Internet begegnet, oder? REST ist also nichts anderes als die Abstraktion des uns bekannten Verhaltens des World Wide Web. Wir rufen einen URI auf und erhalten – neben der einen oder anderen Information – eine Liste von Links, die uns zeigen, welche Operationen genau in diesem Moment möglich beziehungsweise erlaubt sind.

Fragt man zum Beispiel bei einem API einen Ausschnitt einer größeren Datenmenge an, so wären sicherlich neben den reinen Daten im Response zusätzliche Link-Header mit den möglichen Links zum Vor-und Zurücknavigieren innerhalb der Datenmenge sinnvoll. Als Anwender des API müsste ich so im Vorfeld nicht die exakten Link-URIs zum Navigieren kennen, sondern mir nur der Tatsache bewusst sein, dass sich hinter der Referenz der Links previous und next die URIs für das Zurück- und das Vorwärtsblättern befinden. Es gibt mit dem RFC 5988 seit 2010 übrigens sogar einen Standard, der entsprechend Linknamen und deren Semantik festlegt. Also bitte nicht das Rad neu erfinden, sondern wenn möglich und sinnvoll einen der dort aufgeführten Linknamen verwenden.

Bei der Navigation durch eine große Datenmenge ist ja noch vorstellbar, dass das API sinnvolle generische Links für vor, zurück, erste oder letzte Seite liefern kann. Aber wie soll das bei den normalen Ressourcen eines RESTful API funktionieren? Stellen wir uns einmal ein API namens RESTBUCKS vor, mit dem wir Kaffee bestellen und bezahlen können. Das Beispiel stammt übrigens aus dem wirklich guten Buch „Rest in Practice“ [2]. Der initiale Aufruf des einzigen uns bekannten URI http://api.restbucks.com/ würde uns eine HTTP-Response mit dem Code 204 (No Content) liefern sowie einen Link-Header mit der Bezeichnung edit und dem URI http://api.restbucks.com/orders. Aufgrund des RFCs wissen wir, dass wir mit diesem URI eine Bestellung aufgeben, verändern oder löschen können. Natürlich ist auch ein Abfragen des Bestellstatus einer ausstehenden Bestellung mit dem Link möglich.

Entscheidend für das Verständnis ist, dass wir natürlich schon vorher wussten, dass man bei RESTBUCKS Kaffeebestellungen aufgeben kann, und auch, dass wir das notwendige JSON-Format kennen. Der dafür zu verwendende URI ist uns aber nicht bekannt und eigentlich auch egal. Geben wir nun mithilfe dem zurückgelieferten URI eine Bestellung auf, bekommen wir neben der Bestätigung (Status 204 „No Content“ oder 200 „Ok“ inklusive dem Echo der Bestellung, eventuell erweitert um zusätzliche serverseitig angereicherte Informationen) wieder Link-Header zurück, die uns signalisieren, was man nun mit der eben aufgegebenen Bestellung anstellen kann. Diese Link-Header unterscheiden sich – zumindest in Teilen – von den vorherigen, da wir durch unsere Bestellung den State der Anwendung verändert haben.

Ein Link-Header ist typischerweise „self“ mit dem Verweis auf die eben angelegte Ressource. In unserem Fall wäre das zum Beispiel http://api.restbucks.com/orders/123. Mithilfe dieses Links kann man nun jederzeit den Status der Bestellung abfragen. Ein weiterer Link-Header mit der Bezeichnung payment – ebenfalls Bestandteil des RFC 5988 – könnte uns signalisieren, wie wir die offene Bestellung bezahlen können: http://api.restbucks.com/payments/123. Je nach gerade ausgeführter Aktion und dem damit verbundenen serverseitigen Änderungen an dem Application State führen uns die Links also Schritt für Schritt durch die Anwendung oder in unserem Fall durch die möglichen Use Cases des Bestellprozesses.

If it’s not REST, …
Mal Hand auf Herz, wer von uns geht in all seinen RESTful APIs soweit, wie eben in dem RESTBUCKS-Szenario beschrieben? Ich denke einmal die wenigsten. Aber wie ist nun die eigene API auf der nach oben offenen REST-Richterskala einzuordnen? Darf ich überhaupt noch das Wort REST im Zusammenhang mit meinem API in den Mund nehmen ohne dabei einen Shitstorm aus der Ecke der REST-Puristen zu riskieren?

Als kleinen Lackmustest für die Einordnung des eigenen Web Service lässt sich sehr schön das „Maturity Model“ von Leonard Richardson heranziehen. Richardson klassifiziert in seinem Modell Web Services je nach ihrem Support von URIs, HTTP und Hypermedia in drei unterschiedliche REST-Reifegrade. Eigentlich sind es sogar vier, da es unterhalb der drei legitimen Level noch einen Level 0 gibt, der lediglich XML oder JSON via HTTP-Post über die Leitung schickt und somit eher dem klassischem RPC-Modell entspricht. Dieser Level wird auch gerne mit RESTless bezeichnet, da es mit REST so gut wie keine Gemeinsamkeiten aufweist.

In Level 1 führt Richardson die von REST bekannten Ressourcen und somit unterschiedliche Endpoints pro Ressource ein. In einer Anwendung existieren bei diesem Modell in der Regel mehrere URIs. Allerdings wird weiterhin nur eine HTTP-Methode (meist POST) verwendet und auch sonst auf die erweiterten Möglichkeiten des HTTP-Protokolls verzichtet, wie Header, Return-Codes oder Caching. Level 2 setzt genau da an, wo Level 1 aufhört. Den unterschiedlichen HTTP-Methoden werden Operationen auf den Ressourcen zugeordnet: Abfragen der Ressource via GET, Anlegen via POST oder PUT, Ändern via PUT, partielles Ändern via PATCH und Löschen via DELETE. Und auch die HTTP-Statuscodes werden sinnvoll verwendet, um so zu signalisieren, was auf dem Server mit den Ressourcen passiert ist – oder eben nicht. Erst in Level 3 – aka „the glory of REST“ – führt Richardson Hypermedia und somit ein sich selbsterklärendes System ein.

Fazit
Ok, einigen wir uns also darauf, dass – nach der reinen Lehre – wahrscheinlich die wenigsten von uns bisher den heiligen REST-Olymp erklommen haben. Aber ist das wirklich so schlimm? Wichtig ist doch, dass am Ende ein sprechendes API entsteht, das für den Anwender – also den Entwickler – verständlich ist und eine entsprechende Stabilität mit sich bringt. Vinay Sahni, Gründer von Enchant, schrieb dazu einmal in seinem Block: „An API is a developer’s UI – just like any UI, it’s important to ensure the user’s experience is thought out carefully!“. Treffender kann man es wohl kaum formulieren.

Aber darf ich meine APIs nun überhaupt RESTful nennen, wenn ich nach Richardson lediglich ein Level unterhalb von 3 erreicht habe? Ich persönlich bin da eher pragmatisch als religiös veranlagt. Wenn mein API die Bedingungen von Level 2 erfüllt, dann würde ich es durchaus als RESTful bezeichnen. Bei Level 1 dagegen eher nicht.

Was aber, wenn ich auf einen bekennenden Level-3-Vertreter treffe, der keine Wahrheit neben der seinen akzeptiert? Oder anders formuliert: Sollte ich in einer solchen Situation auf Teufel komm raus darauf bestehen, ein RESTful API entworfen zu haben. Vielleicht fehlt uns einfach nur ein Terminus für die Formel „RESTful minus HATEOAS“? Wie wäre es mit RESTalike? In diesem Sinne: Stay tuned…

Geschrieben von
Lars Röwekamp
Lars Röwekamp
Lars Röwekamp ist Gründer des IT-Beratungs- und Entwicklungsunternehmens open knowledge GmbH, beschäftigt sich im Rahmen seiner Tätigkeit als „CIO New Technologies“ mit der eingehenden Analyse und Bewertung neuer Software- und Technologietrends. Ein besonderer Schwerpunkt seiner Arbeit liegt derzeit in den Bereichen Enterprise und Mobile Computing, wobei neben Design- und Architekturfragen insbesondere die Real-Life-Aspekte im Fokus seiner Betrachtung stehen. Lars Röwekamp, Autor mehrerer Fachartikel und -bücher, beschäftigt sich seit der Geburtsstunde von Java mit dieser Programmiersprache, wobei er einen Großteil seiner praktischen Erfahrungen im Rahmen großer internationaler Projekte sammeln konnte.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.