REST - Representational State Transfer

Das witzige ist:

Jeder braucht heute eine REST Schnittstelle, doch leider versteht jeder etwas anderes darunter.

Und das macht mich REST-los unglücklich.


Der Stil der 70er Jahre ist leider auch heute noch weit verbreitet:

  • Globale Variablen
  • Singletons
  • Hart kodierte Pfade
  • Funktionen mit versteckte Zuständen (wie z.B. strtok())
  • und vieles mehr.

Und als Netzwerke, das Internet und das Web aufkamen, übersetzte man diese “Denkfehler” teils 1:1 in das neue Medium.

Doch im Netz sind solche Konstrukte viel störungsanfälliger, als im lokalen Prozess auf meiner Maschine.

Exakte Adressierung

Ein Dienst wird gerne mit einem start() begonnen und mit einem stop() zum Beenden gebracht. Das implementiert man auch gerne genau so, mit einem globalen start() und stop() und verlässt sich dann darauf, dass es genau einen Anwender gibt, der “genau weiß”, wann er etwas starten und wieder stoppen will.

Wenn aber ein zweiter Akteur auftritt, der zuerst stop() und dann start() durchführt, nachdem der erste nur start()en wollte, ist dann das finale stop() des ersten genau das, was er wollte? Denn er stoppt nicht das, was er gestartet hatte, sondern etwas, was ein anderer begonnen hatte.

Solche Seiteneffekte sollten durch REST abgeschafft werden, und eine aktualisierte API kann dann z.B. so aussehen:

  • start(my_token) und stop(my_token)
  • stop_token = start() und stop(stop_token)
  • Oder wie man es in HTTP-Request ausdrücken könnte:
    • POST /service/my_token und DELTE /service/my_token
    • my_token:= POST /service/new und DELETE /service/my_token

(REST != HTTP) && (HTTP != REST)

Viele Leute glauben, REST wäre bereits der Einsatz von GET, POST, PUT und DELETE und jede HTTP-URL wäre bereits eine REST-Schnittstelle.
… Ähm: Nein!

Es kommt darauf an, dass man das, was man auslöst, so parametrisiert, dass im Idealfall keine der beiden Seiten sich merken muss, was die Gegenseite schon getan hat.
Oder anders gesagt: Es müssen alle notwendigen Daten hin- und zurückübertragen werden.

Das geht auch dann, wenn man z.B. nur per POST über HTTP kommuniziert und z.B.: XML-RPC, JSON oder Protobuf Objekte überträgt.
Wichtig ist vor allem, dass der Inhalt der Objekte vollständig ist und keine Seiteneffekte durch gespeicherte Zustände auftreten können.

Der Einsatz von HTTP Methoden macht Aufrufe allerdings “schön” lesbar und kann auch leicht in Funktionsaufrufe übersetzt werden:

HTTP-REST Pseudo-Funktion
GET /service/data_file service.ReadContent(data_file)
POST /service/new_data_file service.AddContent(new_data_file, content)
PUT /service/data_file service.WriteContent(data_file, content)
DELETE /service/data_file service.DeleteContent(data_file)

(Und wer REST wirklich ernst nimmt, der nutzt natürlich die URL /service/service_id/data_file)

Versionierung

APIs brauchen Versionen, weil sie sich immer weiterentwickeln und der übliche HTTP-REST-Weg ist, die Version in der URL als Teil des Pfades anzugeben, z.B.:
/v1/service/something

Doch wie genau man das macht, ist nicht vorgeschrieben. Und das führt auch leider zu manch seltsamen Erscheinungen. Erst neulich musste ich so etwas in meiner Firma entdecken:

  • Endpoint: POST /service/process
    • Protobuf content message can be v1 or v2
  • Endpoint: GET /service/is-v2
    • 200 OK: v2 Protobuf messages in /service enabled
    • Else: /service must use v1 Protobuf messages.

Naja … hier sehe ich eindeutig, dass jene Kollegen ein anderes Verständnis von REST haben, als ich.

Fazit

Ich kann nur allen Entwicklern anraten, sich die APIs der großen Hersteller im Web anzusehen, wie die ihre Schnittstellen aufgebaut haben. Da ist sicher nicht alles perfekt, aber da wurde schon eher auf Konsistenz geachtet.

Das Problem sind die unzähligen Firmen und Konzerne, die intern ihr eigenes Süppchen kochen, das nicht ins Netz gelangt und daher nie “ordentlich” durchgedacht wurde.

Die Meinung, “irgend einen HTTP Endpoint könne man mit REST gleichsetzen”, ist falsch und dennoch weit verbreitet, und produziert umgebungs-ignorante Schnittstellen die schwer zu warten sind, weil sie keinem logischen Muster folgen.

Und das größte Problem ist, dass auch in neuen Produkten diese falschen Ansätze immer weiter verschleppt werden.
Es liegt also noch viel Arbeit vor mir bzw. vor uns, damit solche Legacy-Schnittstellen möglichst bald durch “echtes REST” abgelöst werden.

Also: Auf ans Werk!