Zum Inhalt springen

OBS/Kostenpflichtige Module/RESTServer/Endpunkte

Aus OBS Wiki
Version vom 25. September 2026, 10:28 Uhr von MINERVA-Rademacker (Diskussion | Beiträge) (Idempotency-Key als Pflicht, Aufraeumen offener Reservierungen berichtigt, Fehlercodes 400/503/308 ergaenzt [Volltext ersetzt])
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Endpunkte

Ein Endpunkt stellt eine Adresse bereit, über die der REST-Server konkrete Funktionen anbietet. Jeder Endpunkt wird durch ein OBS-Skript oder einen WebHook realisiert und ist genau einem Server-Profil zugeordnet.

Routing über Pfad-Templates

Das Routing erfolgt über die Spalte Pfad-Template (re_pathtemplate). Ein Template beschreibt den vollständigen Pfad nach dem Host und kann Platzhalter enthalten:

  • Statische Segmente müssen exakt übereinstimmen (Groß-/Kleinschreibung wird ignoriert).
  • Platzhalter in geschweiften Klammern – z. B. {uid} – passen auf einen beliebigen Wert und werden als Pfad-Parameter erfasst.

Beispiele für gültige Templates:

Template Passt auf Pfad-Parameter
/orders /orders –
/orders/{uid} /orders/4711 uid = 4711
/orders/{uid}/modules/{code} /orders/4711/modules/A1 uid = 4711, code = A1
/kalender/v1 /kalender/v1 –

Präzedenz bei mehreren Treffern

Passen mehrere Templates auf denselben Pfad, gewinnt das spezifischste – also das mit den meisten statischen Segmenten. So schlägt /orders/summary das Template /orders/{uid}, während /orders/4711 auf /orders/{uid} matcht.

Hauptfelder eines Endpunkts

Feld Spalte Zweck
Pfad-Template re_pathtemplate Maßgeblich fürs Routing. Vollständiger Pfad mit Platzhaltern, z. B. /orders/{uid}/modules/{code}
Server re_server Zuordnung zum Server-Profil (erforderlich)
Aktiv re_aktiv Statusflag; inaktive Endpunkte liefern 404
Skript re_script DwScript-Quelltext des Handlers (siehe Scripting)
WebHook re_webhook optional: Verweis auf einen Einmal-Endpunkt statt Skript
Info – Dokumentations-Freitext
Datei-Upload re_upload Erlaubt Datei-Uploads an diesem Endpunkt (0 = aus, 1 = an). Ohne Freigabe werden Upload-Anfragen mit 415 abgelehnt.
Max. Upload-Grösse re_upload_size Maximale Dateigrösse in MB. Leer/0 = Standard 25 MB. Überschreitung wird mit 413 abgelehnt.

Pfad-Parameter im Skript

Die aus den Platzhaltern erfassten Werte liest das Endpunkt-Skript über oReader.Path:

procedure Get(oReader: TxRestReader; oWriter: TxRestWriter);
var cUid: string;
begin
    cUid := oReader.Path('uid');   // aus /orders/{uid}
    // ...
end;

Diese Werte stammen aus dem Routing und können nicht durch den Client überschrieben werden.

Datei-Uploads

Ein Endpunkt nimmt Datei-Uploads nur entgegen, wenn er dafür freigeschaltet ist (re_upload = 1). Andernfalls werden Upload-Anfragen mit 415 Unsupported Media Type abgelehnt und das Skript wird nicht ausgeführt.

Die maximal zulässige Dateigrösse wird pro Endpunkt über re_upload_size (in MB) festgelegt; ohne Angabe gilt der Standard von 25 MB. Wird das Limit überschritten, antwortet der Server mit 413 Payload Too Large. Für Uploads gilt nicht das JSON-Body-Limit (10 MB), sondern dieses Endpunkt-Limit. Der Originalname (beim resumable Upload über den Header Upload-Metadata) ist Pflicht; fehlt er, wird der Upload mit 400 abgelehnt, ohne dass eine Datei angelegt wird. Das Skript liest Pfad, Name, Content-Type und die SHA-256-Prüfsumme der gespeicherten Datei über oReader.UploadPath(), UploadName(), UploadType() und UploadSha(); bei multipart/form-data die übrigen Formularfelder über oReader.Form('<name>').

Das Übertragungsprotokoll - Header, Statuscodes, Resume-Verhalten - steht auf einer eigenen Seite: Datei-Upload.

Unterstützt werden zwei Übertragungsarten:

  • Einfacher Upload per multipart/form-data (eine Datei pro Request).
  • Resumable/Chunked Upload per Content-Range (grosse Dateien, fortsetzbar nach Abbruch).

Der Server legt die hochgeladene Datei in einem temporären Verzeichnis ab und übergibt dem Endpunkt-Skript den Pfad. Was mit der Datei geschieht (Ablage, DMS-Verknüpfung, Weiterverarbeitung), entscheidet allein das Skript. Details und Beispiele: Scripting.

Datei-Downloads

Ein Endpunkt kann eine Datei statt eines JSON-Körpers zurückgeben - PDF, CSV, eine APK. Dafür ist keine Freischaltung nötig: ein Download entsteht dadurch, dass das Skript ihn mit oWriter.SendFile(...) bzw. oWriter.SendTempFile(...) erzeugt, nicht dadurch, dass ein Client ihn anfragt.

Content-Type, Dateiname, Bereichsanfragen (Range, 206), ETag und das Aufräumen temporärer Dateien übernimmt der Server. Einzelheiten: Datei-Download.

HTTP-Methode

Welche Funktion aufgerufen wird, ergibt sich aus dem HTTP-Verb: Der Server ruft die gleichnamige Skript-Methode auf (Get/Post/Put/Delete/Patch). Ein Template entspricht damit einem Endpunkt-Skript; unterschiedliche Pfad-Formen (z. B. /orders vs. /orders/{uid}) sind eigene Endpunkt-Einträge mit eigenem Skript und eigener Berechtigung.

URL-Struktur

http://[Host][:Port][Pfad-Template]

Beispiele:

Versionierung

Da es kein eigenes Versions-Feld mehr gibt, wird die Version als statisches Segment ins Template aufgenommen, z. B. /orders/v1 oder /v1/orders/{uid}. Änderung ohne Breaking Change:

  1. Bestehenden Endpunkt unverändert lassen.
  2. Neuen Endpunkt mit gleichem Ressourcennamen, aber neuem Versions-Segment im Template anlegen.
  3. Berechtigungen für neue Konsumenten setzen.
  4. Alten Endpunkt deaktivieren, wenn die Migration abgeschlossen ist.

Zugriffskontrolle

Endpunkte sind Server-Profilen zugeordnet; ein Zugang benötigt eine explizite Berechtigung (Tabelle RESTSRV_ACCESS), um einen Endpunkt nutzen zu dürfen.

ACHTUNG: Ein Endpunkt besteht aus zwei Zeilen, und beide werden von Hand

gepflegt: der Eintrag in RESTSRV_ENDPOINTS und die Berechtigung in RESTSRV_ACCESS. Fehlt die zweite, antwortet der Endpunkt 403 - und sieht dabei fertig aus: Pfad, Skript und Server-Profil stehen korrekt da, der Aufruf kommt trotzdem nicht durch. Das ist die häufigste Ursache für ein „der Endpunkt ist doch angelegt" und kostet jedes Mal eine Fehlersuche, weil der Statuscode nach einem Rechteproblem des Konsumenten aussieht und nicht nach einer fehlenden Konfigurationszeile.

Beim Anlegen also immer beide Zeilen, und beim Prüfen eines

403 zuerst nachsehen, ob die zweite existiert.

Weil jede Pfad-Form ein eigener Endpunkt-Eintrag ist, lässt sich der

Zugriff pro Pfad-Form granular vergeben (z. B. Lesen von /orders/{uid} erlauben, aber das ändernde PUT /orders/{uid}/modules/{code} nicht).

Caching

Die Endpunkt-Definitionen werden pro Server-Profil zwischengespeichert (TTL, Standard 60 s). Neue oder geänderte Endpunkte/Templates werden daher erst nach Ablauf des Caches (bzw. nach einer Cache-Invalidierung) wirksam – nicht zwingend sofort.

HTTP-Fehlercodes

Code Ursache
404 Kein Template passt zum Pfad, Endpunkt inaktiv oder falsches Server-Profil
403 Zugang fehlt in der Berechtigungsliste
401 API-Key ungültig/fehlend, JWT abgelaufen oder Sitzung gesperrt (AUTH_EXPIRED)
500 Skript- oder Syntax-Fehler (Details in RESTSRV_PROTO)
405 Das Endpunkt-Skript hat für die angefragte HTTP-Methode keine Funktion; der Header Allow nennt die vorhandenen
206 Teilantwort eines Datei-Downloads auf eine Range-Anfrage; die Antwort enthält Content-Range
416 Der angefragte Range liegt hinter dem Dateiende; die Antwort nennt in Content-Range die wirkliche Grösse
415 Datei-Upload an einen Endpunkt, der dafür nicht freigeschaltet ist (re_upload = 0); oder ein Content-Encoding, das der Server nicht entpacken kann - unterstützt wird allein gzip (siehe REST-Server, Abschnitt Kompression)
413 Hochgeladene Datei überschreitet die zulässige Maximalgrösse (re_upload_size); oder der Anfragekörper überschreitet 10 MB bzw. - gepackt gesendet - das 400fache der gesendeten Grösse
429 Rate-Limit überschritten; die Antwort enthält den Header Retry-After
503 Vier Ursachen, unterscheidbar am code: IDEMPOTENCY_IN_PROGRESS (eine Anfrage mit demselben Idempotency-Key wird gerade verarbeitet), SERVICE_UNAVAILABLE bei erreichter Andrangsgrenze des Server-Profils (rsv_max_parallel), bei einer Anmeldung, deren Sitzungszeile nicht geschrieben werden konnte, sowie bei einem fehlgeschlagenen JwtRevoke. Alle vier tragen Retry-After
409 Ein früherer Aufruf mit demselben Idempotency-Key ist ohne Ergebnis geblieben (IDEMPOTENCY_UNRESOLVED)
422 Derselbe Idempotency-Key wurde mit anderem Inhalt gesendet (IDEMPOTENCY_KEY_REUSED)
400 Ein schreibender Aufruf (POST/PUT/PATCH/DELETE) ohne Idempotency-Key (IDEMPOTENCY_KEY_MISSING); ein resumable Upload ohne Dateinamen; oder eine Anfrage, die unverschlüsselt an einen TLS-Port geschickt wurde (http statt https) - letztere wird vor Authentifizierung und Endpunkt-Skript abgewiesen
308 Zwischenantwort beim resumable Upload: das Teilstück wurde angehängt, der Upload ist noch nicht vollständig. Die Antwort trägt Upload-Id, Upload-Offset und Range; das Skript läuft dabei nicht

Jede Fehlerantwort besteht aus einem error-Objekt mit maschinenlesbarem Code - Aufbau siehe Übersicht.

Daneben kann ein Endpunkt-Skript den Statuscode selbst setzen (z.B. 201, 204, 409, 422) sowie Response-Header wie ETag oder Location - siehe Scripting.

Idempotenz

Bei POST, PUT, PATCH und DELETE sorgt der Server selbst dafür, dass eine wiederholte Sendung keine Zweitwirkung hat. Das gilt für jeden Endpunkt - es muss weder am Endpunkt etwas eingestellt noch im Skript etwas programmiert werden.

ACHTUNG: Der Header Idempotency-Key ist bei diesen vier Verben

Pflicht. Fehlt er, antwortet der Server mit 400 IDEMPOTENCY_KEY_MISSING und das Endpunkt-Skript läuft nicht an.

Bis v1.21 lief eine Anfrage ohne den Header ungeschützt durch; der Schutz gegen die Doppelbuchung lag damit beim Client. Ausgenommen ist allein der JWT-Endpunkt (Anmelden, Erneuern, Abmelden) - er greift vor der Adressauflösung

und kennt die Mechanik nicht.
Situation Antwort des Servers
Kein Idempotency-Key gesendet 400 IDEMPOTENCY_KEY_MISSING. Das Skript läuft nicht
Erster Aufruf Endpunkt läuft normal, das Ergebnis wird zum Schlüssel gespeichert
Wiederholung, gleicher Inhalt Gespeicherte Antwort (gleicher Statuscode, gleicher Body, gleiche Header) plus Header Idempotent-Replay: true. Das Skript läuft nicht
Wiederholung, anderer Inhalt 422 IDEMPOTENCY_KEY_REUSED - derselbe Schlüssel für einen anderen Inhalt ist ein Client-Fehler
Erster Aufruf läuft noch 503 IDEMPOTENCY_IN_PROGRESS mit Retry-After. Beim nächsten Versuch liegt die gespeicherte Antwort vor
Früherer Aufruf ohne Ergebnis 409 IDEMPOTENCY_UNRESOLVED. Nur noch nach einem harten Abbruch des Dienstes zwischen Verarbeitung und Festschreiben; ob die Buchung stattgefunden hat, ist unbekannt. Der Aufräumlauf gibt solche Einträge nach 24 Stunden frei und protokolliert sie

Wichtig für die Auslegung eines Endpunkts:

  • Antwortet das Skript mit 2xx, wird die Antwort gespeichert und bei einer Wiederholung erneut ausgeliefert.
  • Ausnahme Dateiantwort: Liefert das Skript mit SendFile/SendTempFile eine Datei aus, wird nichts festgeschrieben - eine Dateiantwort liesse sich nicht wiedergeben, gespeichert würde eine leere 200er-Antwort. Der Schlüssel wird stattdessen wieder freigegeben, ein erneuter Abruf derselben Datei ist unschädlich.
  • Antwortet das Skript mit 4xx (fachliche Ablehnung), wird der Schlüssel wieder freigegeben - der Konsument darf ihn nach Korrektur erneut verwenden. Sonst würde ein einziger Validierungsfehler den Schlüssel dauerhaft blockieren.
  • Endet die Verarbeitung mit 5xx oder einem Skript-Fehler, wird der Schlüssel freigegeben und der Fall protokolliert. Der Konsument darf denselben Schlüssel erneut senden.
  • Das ist eine bewusste Abwägung (geändert 2026-08-25): Bleibt der Schlüssel nach einem Serverfehler belegt, ist der Vorgang unwiederholbar - der Client bekommt dauerhaft 503 und müsste die erfasste Arbeit verwerfen. Garantierter Datenverlust wiegt schwerer als ein möglicher Doppelsatz.
  • Folge für die Auslegung: ein schreibender Endpunkt sollte selbst wiederholbar sein, etwa über ein fachliches Merkmal des Clients. Wo reines Anhängen stattfindet (Positionen, Dateien), bleibt sonst ein Restrisiko.

Der Schlüssel gilt je Zugang, Methode und Pfad. Derselbe Idempotency-Key an einem anderen Endpunkt ist damit ein eigener Vorgang - der Konsument muss ihn nicht global eindeutig vergeben, aber pro Vorgang stabil wiederverwenden (also nicht bei jedem Wiederholversuch neu erzeugen).

Die Einträge stehen in RESTSRV_IDEMPOTENCY. Abgeschlossene Einträge werden nach 30 Tagen gelöscht. Einträge ohne Ergebnis - sie entstehen nur bei einem harten Abbruch des Dienstes - werden ab 24 Stunden im Protokoll gemeldet und anschliessend ebenfalls gelöscht. Das Stehenlassen wäre keine Vorsicht, sondern eine Sperre: derselbe Vorgang mit demselben Schlüssel wäre sonst nie wieder durchführbar.

WebHooks

WebHooks sind Einmal-Endpunkte für asynchrone Callbacks. Beim ersten erfolgreichen Aufruf wird die Antwort in der Tabelle REMOTE_HOOK_URL gespeichert und der Endpunkt anschließend automatisch gelöscht.

Typischer Einsatz: OBS stösst einen Vorgang bei einem Fremdsystem an (Bezahldienst, Versanddienstleister) und gibt diesem eine Rückruf-Adresse mit, die nur ein einziges Mal gültig ist. Damit kann die Adresse nicht später erneut - oder von jemand anderem - benutzt werden.

Ablauf:

  1. OBS legt einen Eintrag in REMOTE_HOOK_URL an und dazu einen Endpunkt, dessen Feld WebHook (re_webhook) auf diesen Eintrag zeigt. Ein Skript wird für diesen Endpunkt nicht gepflegt.
  2. Die Adresse dieses Endpunkts wird dem Fremdsystem als Callback-URL übergeben.
  3. Das Fremdsystem ruft die Adresse auf. Der Server legt den übergebenen Inhalt in REMOTE_HOOK_URL.hu_response ab, antwortet mit {"status": "ok"} und löscht den Endpunkt.
  4. Der weiterverarbeitende OBS-Prozess findet die Rückmeldung in hu_response.
HINWEIS: Ein zweiter Aufruf derselben Adresse läuft ins Leere: Der Endpunkt

existiert nicht mehr, die Antwort ist 404. Das ist gewollt - ein WebHook ist keine dauerhafte Schnittstelle. Für eine dauerhaft erreichbare Rückmelde-Adresse einen

normalen Endpunkt mit Skript anlegen.