REST - Representational State Transfer
« | 26 Nov 2022 | »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)
undstop(my_token)
stop_token = start()
undstop(stop_token)
- Oder wie man es in HTTP-Request ausdrücken könnte:
POST /service/my_token
undDELTE /service/my_token
my_token:= POST /service/new
undDELETE /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.
- 200 OK: v2 Protobuf messages in
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!