Einheitliche Schnittstelle
Vielleicht die ungewohnteste Restriktion des REST-Ansatzes ist die einheitliche ("uniforme") Schnittstelle. Sie ist eine logische Konsequenz aus dem Anspruch, Anwendungen in ein Web von vernetzten Ressourcen zu transformieren. REST postuliert, dass jede Ressource die gleichen Operationen unterstützen muss. Im HTTP-Protokoll wird diese allgemeine Schnittstelle zu den bekannten Methoden GET, PUT, POST und DELETE (sowie u.a. den weniger bekannten HEAD und OPTIONS) konkretisiert. Natürlich verhalten sich Ressourcen unterschiedlich, aber sie haben einen gemeinsamen Kontrakt, der in der HTTP-Spezifikation definiert wird. Eine spezifische Ressource, zum Beispiel eine Kundenliste, kann von einem darauf spezifisch zugeschnittenen Client genauso genutzt werden wie ein mit einer anderen Technologie realisierter Dienst (Abb.1). Implementiert die Ressource das HTTP-Interface korrekt, kann sie jedoch auch von einem generischen Client benutzt werden – sei es ein Browser, ein Kommandozeilenwerkzeug wie curl oder wget oder ein externer Dienst wie Google oder Yahoo!.
Was genau legt die HTTP-Spezifikation als Semantik der einzelnen Methoden fest? Da die generische Schnittstelle auf alle Ressourcen anwendbar sein muss, ist sie naturgemäß allgemein gehalten. Sie definiert GET als eine lesende Operation, die "sicher" ist. Das heißt, dass ein Client durch ein HTTP GET keine Verpflichtung eingeht. Für GET ist außerdem ein sehr mächtiger Caching-Mechanismus spezifiziert, der eine feingranulare Kontrolle erlaubt. PUT legt eine neue Ressource unter einem bekannten Namen an bzw. aktualisiert sie dort. Ebenso wie DELETE und GET ist PUT idempotent: Weiß der Client nicht, ob die Operation erfolgreich war oder nicht, kann er die Operation gefahrlos wiederholen. Ein POST legt eine untergeordnete Ressource an und ist die einzige Operation, für die es keine Garantien gibt.
Die Herausforderung für den Entwickler einer REST-konformen Anwendung besteht vor allem darin, die auf andere Architekturen wie CORBA, RMI, DCOM oder auch Web Services zugeschnittenen Gewohnheiten abzulegen. Es wird nicht für jeden Service eine eigene Schnittstelle entworfen, mit ganz spezifischen Operationen und Datentypen. Stattdessen muss die applikationsspezifische Funktionalität auf die uniforme Schnittstelle abgebildet werden. Im Gegensatz dazu wird bei SOAP/WSDL – ähnlich wie bei CORBA, DCOM und anderen Vorgängern – für jede Applikation eine neue, eben applikationsspezifische Schnittstelle erfunden. Dementsprechend können auch nur solche Systeme diese Schnittstelle nutzen, die explizit dafür entwickelt wurden.
Beispiel: Serienbriefe
Sehen wir uns zur Verdeutlichung ein konkretes Beispiel an: den Entwurf einer Schnittstelle für einen Serienbriefservice, einen Dienst für den Versand eines einheitlichen, ggf. personalisierten Texts an eine Vielzahl von Empfängern. In einem klassischen Entwurf würden Sie vielleicht zunächst die dienstspezifische Schnittstelle entwerfen, mit Operationen wie Vorlage anlegen, Brief versenden, Methoden zur Verwaltung von Adressen usw. Bei näherer Betrachtung ist diese Schnittstelle jedoch gar nicht so spezifisch wie man zunächst glaubt: Viele der zentralen Objekte werden vom Dienst (oder dem System, das den Dienst anbietet – je nach Terminologievorliebe) verwaltet, müssen also dort angelegt, geändert, gelöscht und ausgelesen werden. Diese CRUD-Funktionalität ist recht leicht abzubilden. Bei der REST- Variante unseres Beispieldienstes würden wir dazu vielleicht URIs der in Listing 1 dargestellten Form definieren.
Wir haben damit zunächst eine Art Stammdatenverwaltung für die Serienbriefe erstellt, die allerdings nun aufgrund der generischen Schnittstelle nicht nur von einem spezifischen Client, sondern auch von einem allgemeingültigen wie z.B. dem Browser verwendet werden kann. Voraussetzung dafür ist, dass das Format, der Content Type, bekannt ist – kleinster gemeinsamer Nenner ist HTML oder XML, aber auch ganz spezifische Formate wie z.B. VCard, eine standardisierte XML-Instanz, RSS oder Atom können sinnvoll sein. Und da eine Ressource mehrere Repräsentationen haben kann, lassen sich diese sinnvoll kombinieren – ein Standardformat für den generischen, ein spezifisches für einen genau auf die Anwendung zugeschnittenen Client.
Darüber hinaus müssen wir nun alle Operationen auf eine der Standardoperationen abbilden, die für Ressourcen zur Verfügung stehen. Aus der Operation Brief versenden wird damit Versandauftrag anlegen. Dieser Auftrag hat nun eine eindeutige ID (URI) und kann als Link-Ziel dienen, sein Status kann abgefragt werden, auch eine Änderung ist möglich (wenn auch sicher nur so lange, wie er noch nicht ausgeführt wurde). Die eigentliche Briefproduktionsmaschinerie kann ihre Aufträge abfragen, indem sie sich per GET über die Liste der noch offenen Aufträge informiert und den jeweils nächsten anstehenden verarbeitet. Neu angelegte oder abgeschlossene Aufträge könnten gleichzeitig als Newsfeed zur Verfügung gestellt werden, sodass man sich mit einem Standardnewsreader über den aktuellen Status informieren kann.
Die zentralen Konzepte – oder, wenn Sie möchten, Objekte – unserer Anwendung haben nun einen eigenen, stabilen URI. Diese Eigenschaft machen wir uns zunutze, um die erweiterte Funktionalität umzusetzen: Das Anlegen eines neuen Versandauftrags könnte aus einem POST von Links auf die Adressen, an die versandt werden soll, sowie einer weiteren Verknüpfung zur Vorlage bestehen. Wird ein Brief an eine große Anzahl von Empfängern versandt, könnte stattdessen ein Link auf die Collection-Ressource, die die passende Menge zurückliefert, enthalten sein. Das Spannendste daran ist, dass die einzelnen Bestandteile der Anwendung ebenfalls nur über die uniforme Schnittstelle gekoppelt sind. Eine andere Adressverwaltung als die in unsere Lösung integrierte könnte einfach angebunden werden, wenn sie ebenfalls einzelne URIs für ihre Elemente und den gleichen Content-Typ unterstützt.
Noch Fragen?
Ist REST nun der nächste große Hype nach Web Services? In gewisser Weise ist die Antwort ein klares "Jein". Auf der einen Seite stammt die REST-Dissertation aus dem Jahr 2000, und sie beschreibt die Prinzipien, die dem Design des WWW schon vorher zugrunde lagen. Mit den Worten von Roy Fielding: "SOAP was known to be a bad idea in 1999, but in spite of our comments to this effect, the industry insisted on proving that for themselves". REST gab es also schon lange vor Web- Services. Auf der anderen Seite ist unbestreitbar, dass aus einem Thema, das vor zwei oder drei Jahren noch relativ exotisch war, mittlerweile ein Aufmerksamkeitsmagnet geworden ist. Unabhängig stellt man sich nach einer ersten Beschäftigung mit dem Thema zunächst einige Fragen – irgendwie kann das Ganze nicht funktionieren! Oder doch? Die häufigsten Zweifel möchte ich im Folgenden ansprechen.
REST = CRUD?
Weil REST die Menge von Operationen auf einige wenige (GET, PUT, POST, DELETE) beschränkt, könnte man annehmen, mit REST könne man bestenfalls eine simple Datenverwaltung aufbauen, nicht jedoch fortgeschrittene Anwendungsfälle. Aber REST ist nicht das Gleiche wie CRUD: Ob es sich um eine Web-Service-Operation mit dem Namen submitOrder oder ein POST auf /orders handelt: in beiden Fällen kann und sollte dahinter Geschäftslogik ablaufen und nicht nur ein simples Einfügen eines Datensatzes. Viele Operationen bewirken als Haupteffekt die Änderung oder Neuanlage eines Geschäftsobjekts; für diese Fälle ist die Abbildung auf das generische REST-Interface sehr einfach. Für die zusätzlichen Anwendungsfälle besteht der Trick darin, Aktionen auf eigene Ressourcen abzubilden: aus Verben werden Substantive. Statt einen Status zu ändern, legt man eine neue Statusänderung an, aus cancel subscription wird create SubscriptionCancellation usw. Einige Beispiele für eine solche Abbildung sind in Listing 2 dargestellt. Es erfordert zwar eine gewisse Übung; hat man sich jedoch einmal daran gewöhnt, denkt man ganz automatisch in Ressourcen. Darüber hinaus ergeben sich diverseVorteile – individuelle Aktionen (bzw. ihre Ergebnisse) sind historisierbar, man kann sie verlinken, sie wieder zurücknehmen und die Ergebnisse in unterschiedlichen Formaten zur Verfügung stellen.




