OBS/Kostenpflichtige Module/RESTServer/Scripting: Unterschied zwischen den Versionen
Böhrer (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „Photo Credіt rating Graphic Ƅy Flickr.com, courtesy of Gordana Adamοvic-Mladenovic Mom and dad ϲonsiderably hаve an effect on their children�s habits.<b…“) |
Keine Bearbeitungszusammenfassung |
||
| (5 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
{{Kostenpflichtige Module}} | |||
=Anleitung für Endpunkt-Skripte= | |||
Jede Anfrage an einen Endpunkt wird nach erfolgreicher Authentifizierung und Autorisierung an das hinterlegte Skript weitergegeben. Das Skript ist in Object Pascal geschrieben und greift auf die OBS-Bibliothek (DB-Zugriff, Hilfsfunktionen) zu. | |||
==Sicherheitshinweise== | |||
{{Hinweis|Endpunkt-Skripte verarbeiten Daten aus dem Internet. Übergabeparameter dürfen niemals ungeprüft in SQL-Anweisungen eingebaut werden. Werte immer über ''DB_SQLVal'' bzw. Parameter-Bindings absichern, Eingabewerte gegen Whitelists prüfen.}} | |||
* Eingaben gegen erwartete Werte prüfen (z.B. Pflichtfelder, erlaubte Typen, Wertebereiche). | |||
* Nur das zurückgeben, was der Konsument wirklich braucht - keine internen IDs, keine Sys-Felder, keine Passwörter. | |||
* Bei Fehlern keine internen Details an den Konsumenten zurückgeben; stattdessen schreibt der Server ohnehin Detail-Einträge in '''RESTSRV_PROTO'''. | |||
==Methoden-Signatur== | |||
Pro HTTP-Methode wird im Skript eine gleichnamige Funktion implementiert. Der Server ruft genau die Funktion auf, die zur Methode des eingehenden Requests passt. | |||
function Get (oParams: TStrings; oBody: TJSONObject): string; | |||
function Post (oParams: TStrings; oBody: TJSONObject): string; | |||
function Put (oParams: TStrings; oBody: TJSONObject): string; | |||
function Delete(oParams: TStrings; oBody: TJSONObject): string; | |||
function Patch (oParams: TStrings; oBody: TJSONObject): string; | |||
Nicht implementierte Methoden liefern automatisch ''405''-ähnliche Fehler über die generische Skript-Antwort. | |||
==Parameter== | |||
===oParams (TStrings)=== | |||
Enthält alle Query-Parameter, POST-Parameter (form-urlencoded) sowie die durchgereichten HTTP-Header. Werte sind immer Strings. | |||
Filterung durch den Server: | |||
* Geblockte Header werden nicht durchgereicht: ''authorization'', ''cookie'', ''proxy-authorization'', ''x-forwarded-for'', ''x-real-ip'', ''apikey'', ''api_key''. | |||
* Parameter mit Präfix '''_OBS_''' können von aussen nicht gesetzt werden; sie sind für interne Werte reserviert. | |||
* Werte werden auf max. 1024 Zeichen begrenzt. | |||
* Null-Bytes und Steuerzeichen (ausser Tab, CR, LF) werden entfernt. | |||
Zugriff im Skript: | |||
if (not Empty(oParams.Values['kundennr'])) then begin | |||
cKundenNr := oParams.Values['kundennr']; | |||
end; | |||
===oBody (TJSONObject)=== | |||
Enthält den Request-Body, sofern dieser ein gültiges JSON-Objekt ist. Bei leerem oder nicht-JSON-Body wird ''nil'' übergeben. | |||
cUser := ''; | |||
cPass := ''; | |||
if (Assigned(oBody)) then begin | |||
oVal := oBody.GetValue('username'); | |||
if (Assigned(oVal)) then begin | |||
cUser := oVal.Value; | |||
end; | |||
oVal := oBody.GetValue('password'); | |||
if (Assigned(oVal)) then begin | |||
cPass := oVal.Value; | |||
end; | |||
end; | |||
Maximale Body-Grösse: 10 MB. | |||
===Reservierte _OBS_-Parameter=== | |||
Bei aktiver JWT-Authentifizierung stehen die Token-Claims als Parameter zur Verfügung: | |||
{| class="wikitable" | |||
! Parameter !! Inhalt | |||
|- | |||
| _OBS_JWT_ID || JWT-Id (''jti''-Claim) - typisch die User-Id | |||
|- | |||
| _OBS_JWT_SUBJECT || Subject (''sub''-Claim) - typisch Benutzername | |||
|- | |||
| _OBS_JWT_AUDIENCE || Audience (''aud''-Claim) - typisch Mandant / Rolle | |||
|} | |||
Diese Werte werden vom Server gesetzt und können vom Skript für Berechtigungs- und Mandantenprüfungen verwendet werden. | |||
===Pfad-Parameter=== | |||
Stammt der Endpunkt aus einem Pfad-Template mit Platzhaltern (siehe [[OBS/Kostenpflichtige Module/RESTServer/Endpunkte|Endpunkte]]), stehen die aus den Platzhaltern erfassten Werte als reservierte Parameter mit Präfix '''_OBS_PATH_''' in ''oParams'' bereit: | |||
{| class="wikitable" | |||
! Template !! Zugriff im Skript | |||
|- | |||
| <code>/orders/{uid}</code> || ''oParams.Values['_OBS_PATH_uid']'' | |||
|- | |||
| <code>/orders/{uid}/modules/{code}</code> || ''oParams.Values['_OBS_PATH_uid']'', ''oParams.Values['_OBS_PATH_code']'' | |||
|} | |||
Da der Präfix ''_OBS_'' für von aussen gelieferte Header- und Query-Parameter gesperrt ist, sind diese Werte nicht durch den Client fälschbar. Ein vollständiges Beispiel zeigt [[OBS/Kostenpflichtige Module/RESTServer/Beispiel4|Beispiel 4 - Pfad-Parameter]]. | |||
==Rückgabe== | |||
Die Rückgabe ist immer ein '''JSON-String'''. Wird ein leerer String zurückgegeben, antwortet der Server automatisch mit ''{}''. Der Server setzt Content-Type auf ''application/json; charset=utf-8'' und Status auf 200, sofern das Skript nicht selbst einen Fehler signalisiert. | |||
Einfaches Beispiel: | |||
<syntaxhighlight lang="pascal" line> | |||
function Get(oParams: TStrings; oBody: TJSONObject): string; | |||
var oRes: TJSONObject; | |||
begin | |||
oRes := TJSONObject.Create(); | |||
try | |||
oRes.AddPair('wert_string', '123'); | |||
oRes.AddPair('wert_int' , 456); | |||
result := oRes.ToJSON(); | |||
finally | |||
MyFreeAndNil(oRes); | |||
end; | |||
end; | |||
</syntaxhighlight> | |||
==JWT-Authentifizierungs-Skript== | |||
Bei Zugängen mit aktiver JWT-Pflicht wird über den JWT-Endpunkt das im Zugang hinterlegte Authentifizierungs-Skript aufgerufen (F7 in der Zugänge-Liste). Das Skript muss eine Methode ''Authenticate'' bereitstellen: | |||
<syntaxhighlight lang="pascal" line> | |||
function Authenticate(oParams: TStrings; oBody: TJSONObject): string; | |||
var oRes : TJSONObject; | |||
cUser : string; | |||
cPass : string; | |||
begin | |||
oRes := TJSONObject.Create(); | |||
try | |||
// Eingangsdaten lesen (Body oder Query-Param) | |||
cUser := ''; | |||
cPass := ''; | |||
if (Assigned(oBody)) then begin | |||
oVal := oBody.GetValue('username'); | |||
if (Assigned(oVal)) then begin | |||
cUser := oVal.Value; | |||
end; | |||
oVal := oBody.GetValue('password'); | |||
if (Assigned(oVal)) then begin | |||
cPass := oVal.Value; | |||
end; | |||
end; | |||
// Prüfung gegen eigene Tabelle, Hash-Verfahren, LDAP, ... | |||
if (PasswortPasst(cUser, cPass)) then begin | |||
oRes.AddPair('status' , 1); | |||
oRes.AddPair('_OBS_JWT_ID' , cUser); | |||
oRes.AddPair('_OBS_JWT_SUBJECT', cUser); | |||
oRes.AddPair('_OBS_JWT_AUDIENCE', 'mandant1'); | |||
end else begin | |||
oRes.AddPair('status', 9); | |||
oRes.AddPair('error' , 'Login fehlgeschlagen'); | |||
end; | |||
result := oRes.ToJSON(); | |||
finally | |||
MyFreeAndNil(oRes); | |||
end; | |||
end; | |||
</syntaxhighlight> | |||
Rückgabewerte: | |||
{| class="wikitable" | |||
! status !! Bedeutung | |||
|- | |||
| 1 || Erfolgreich, der Server erzeugt aus den ''_OBS_JWT_*''-Werten einen Token (HS256) | |||
|- | |||
| 9 || Misserfolg, der Server antwortet mit 403 und protokolliert den Wert von ''error'' | |||
|} | |||
==Fehlerbehandlung im Skript== | |||
Tritt im Skript eine Exception auf oder schlägt die Syntax-Prüfung fehl, antwortet der Server mit 500 ''Interner Fehler'' und protokolliert die Detail-Meldung in '''RESTSRV_PROTO''' (mit Skript-Fehlertext). Der Konsument sieht keine internen Details. | |||
Will das Skript einen spezifischen Fehler an den Konsumenten zurückgeben, ist eine eigene JSON-Struktur zu liefern: | |||
<syntaxhighlight lang="pascal" line> | |||
function Post(oParams: TStrings; oBody: TJSONObject): string; | |||
var oRes: TJSONObject; | |||
begin | |||
oRes := TJSONObject.Create(); | |||
try | |||
if (Empty(oParams.Values['kundennr'])) then begin | |||
oRes.AddPair('status', 'error'); | |||
oRes.AddPair('msg' , 'Parameter ''kundennr'' fehlt'); | |||
result := oRes.ToJSON(); | |||
exit; | |||
end; | |||
// ... | |||
finally | |||
MyFreeAndNil(oRes); | |||
end; | |||
end; | |||
</syntaxhighlight> | |||
==Skript-Cache== | |||
Der Server cached das kompilierte Skript pro Endpunkt (Schlüssel: ''sys_date'' des Endpunkts). Zusätzlich werden die Endpunkt-Definitionen pro Server-Profil in einem TTL-Cache (Standard 60 s) gehalten. Eine Skript-Änderung über F7 wird daher erst nach Ablauf dieses TTL (bzw. nach einer Cache-Invalidierung) wirksam - typischerweise innerhalb einer Minute, nicht zwingend sofort. | |||
==Verfügbare Bibliotheken== | |||
Im Skript können alle OBS-Standard-Bibliotheken verwendet werden. Typische Einstiegspunkte: | |||
* ''Base.Tools'' - String-, Datum-, IIF-, Empty-Helper | |||
* ''Base.DB'' / ''Base.xQuery'' - Datenbank-Operationen, ''DB_SQLVal'', ''DB_SOpen'', ''DB_LSeek'' | |||
* ''Base.qSqlReg'' - ''qSqlInit'', ''qSet'', ''SaveData'' | |||
* ''System.JSON'' - JSON-Objekte und -Arrays | |||
* ''Base.ToolsConst'' - Konstanten wie ''CRLF'', ''SINGELQUOTE'', ''EMPTY_DATE'' | |||
Konkrete Beispiele: | |||
* [[OBS/Kostenpflichtige Module/RESTServer/Beispiel1|Beispiel: Daten-Abruf mit JWT]] | |||
* [[OBS/Kostenpflichtige Module/RESTServer/Beispiel2|Beispiel: Datensatz anlegen mit JSON-Body]] | |||
* [[OBS/Kostenpflichtige Module/RESTServer/Beispiel4|Beispiel: Pfad-Parameter im Routing]] | |||
Aktuelle Version vom 15. Juni 2026, 08:24 Uhr
- A Preise aktualisieren
- C Personen übertragen
- E Kategorien verwalten
- G Kataloge verwalten
- I Merkliste übertragen
- K Varianten übertragen
- L Artikelvarianten übertragen
- M Referenzarten übertragen
- N Lagerbestände verwalten
- U Bestellungen einlesen
- V leere Passworte füllen
- W Update-Informationen zurücksetzen
- X Konfiguration
- Z Protokoll
Anleitung für Endpunkt-Skripte
Jede Anfrage an einen Endpunkt wird nach erfolgreicher Authentifizierung und Autorisierung an das hinterlegte Skript weitergegeben. Das Skript ist in Object Pascal geschrieben und greift auf die OBS-Bibliothek (DB-Zugriff, Hilfsfunktionen) zu.
Sicherheitshinweise
- Eingaben gegen erwartete Werte prüfen (z.B. Pflichtfelder, erlaubte Typen, Wertebereiche).
- Nur das zurückgeben, was der Konsument wirklich braucht - keine internen IDs, keine Sys-Felder, keine Passwörter.
- Bei Fehlern keine internen Details an den Konsumenten zurückgeben; stattdessen schreibt der Server ohnehin Detail-Einträge in RESTSRV_PROTO.
Methoden-Signatur
Pro HTTP-Methode wird im Skript eine gleichnamige Funktion implementiert. Der Server ruft genau die Funktion auf, die zur Methode des eingehenden Requests passt.
function Get (oParams: TStrings; oBody: TJSONObject): string; function Post (oParams: TStrings; oBody: TJSONObject): string; function Put (oParams: TStrings; oBody: TJSONObject): string; function Delete(oParams: TStrings; oBody: TJSONObject): string; function Patch (oParams: TStrings; oBody: TJSONObject): string;
Nicht implementierte Methoden liefern automatisch 405-ähnliche Fehler über die generische Skript-Antwort.
Parameter
oParams (TStrings)
Enthält alle Query-Parameter, POST-Parameter (form-urlencoded) sowie die durchgereichten HTTP-Header. Werte sind immer Strings.
Filterung durch den Server:
- Geblockte Header werden nicht durchgereicht: authorization, cookie, proxy-authorization, x-forwarded-for, x-real-ip, apikey, api_key.
- Parameter mit Präfix _OBS_ können von aussen nicht gesetzt werden; sie sind für interne Werte reserviert.
- Werte werden auf max. 1024 Zeichen begrenzt.
- Null-Bytes und Steuerzeichen (ausser Tab, CR, LF) werden entfernt.
Zugriff im Skript:
if (not Empty(oParams.Values['kundennr'])) then begin
cKundenNr := oParams.Values['kundennr'];
end;
oBody (TJSONObject)
Enthält den Request-Body, sofern dieser ein gültiges JSON-Objekt ist. Bei leerem oder nicht-JSON-Body wird nil übergeben.
cUser := ;
cPass := ;
if (Assigned(oBody)) then begin
oVal := oBody.GetValue('username');
if (Assigned(oVal)) then begin
cUser := oVal.Value;
end;
oVal := oBody.GetValue('password');
if (Assigned(oVal)) then begin
cPass := oVal.Value;
end;
end;
Maximale Body-Grösse: 10 MB.
Reservierte _OBS_-Parameter
Bei aktiver JWT-Authentifizierung stehen die Token-Claims als Parameter zur Verfügung:
| Parameter | Inhalt |
|---|---|
| _OBS_JWT_ID | JWT-Id (jti-Claim) - typisch die User-Id |
| _OBS_JWT_SUBJECT | Subject (sub-Claim) - typisch Benutzername |
| _OBS_JWT_AUDIENCE | Audience (aud-Claim) - typisch Mandant / Rolle |
Diese Werte werden vom Server gesetzt und können vom Skript für Berechtigungs- und Mandantenprüfungen verwendet werden.
Pfad-Parameter
Stammt der Endpunkt aus einem Pfad-Template mit Platzhaltern (siehe Endpunkte), stehen die aus den Platzhaltern erfassten Werte als reservierte Parameter mit Präfix _OBS_PATH_ in oParams bereit:
| Template | Zugriff im Skript |
|---|---|
/orders/{uid} |
oParams.Values['_OBS_PATH_uid'] |
/orders/{uid}/modules/{code} |
oParams.Values['_OBS_PATH_uid'], oParams.Values['_OBS_PATH_code'] |
Da der Präfix _OBS_ für von aussen gelieferte Header- und Query-Parameter gesperrt ist, sind diese Werte nicht durch den Client fälschbar. Ein vollständiges Beispiel zeigt Beispiel 4 - Pfad-Parameter.
Rückgabe
Die Rückgabe ist immer ein JSON-String. Wird ein leerer String zurückgegeben, antwortet der Server automatisch mit {}. Der Server setzt Content-Type auf application/json; charset=utf-8 und Status auf 200, sofern das Skript nicht selbst einen Fehler signalisiert.
Einfaches Beispiel:
function Get(oParams: TStrings; oBody: TJSONObject): string;
var oRes: TJSONObject;
begin
oRes := TJSONObject.Create();
try
oRes.AddPair('wert_string', '123');
oRes.AddPair('wert_int' , 456);
result := oRes.ToJSON();
finally
MyFreeAndNil(oRes);
end;
end;
JWT-Authentifizierungs-Skript
Bei Zugängen mit aktiver JWT-Pflicht wird über den JWT-Endpunkt das im Zugang hinterlegte Authentifizierungs-Skript aufgerufen (F7 in der Zugänge-Liste). Das Skript muss eine Methode Authenticate bereitstellen:
function Authenticate(oParams: TStrings; oBody: TJSONObject): string;
var oRes : TJSONObject;
cUser : string;
cPass : string;
begin
oRes := TJSONObject.Create();
try
// Eingangsdaten lesen (Body oder Query-Param)
cUser := '';
cPass := '';
if (Assigned(oBody)) then begin
oVal := oBody.GetValue('username');
if (Assigned(oVal)) then begin
cUser := oVal.Value;
end;
oVal := oBody.GetValue('password');
if (Assigned(oVal)) then begin
cPass := oVal.Value;
end;
end;
// Prüfung gegen eigene Tabelle, Hash-Verfahren, LDAP, ...
if (PasswortPasst(cUser, cPass)) then begin
oRes.AddPair('status' , 1);
oRes.AddPair('_OBS_JWT_ID' , cUser);
oRes.AddPair('_OBS_JWT_SUBJECT', cUser);
oRes.AddPair('_OBS_JWT_AUDIENCE', 'mandant1');
end else begin
oRes.AddPair('status', 9);
oRes.AddPair('error' , 'Login fehlgeschlagen');
end;
result := oRes.ToJSON();
finally
MyFreeAndNil(oRes);
end;
end;
Rückgabewerte:
| status | Bedeutung |
|---|---|
| 1 | Erfolgreich, der Server erzeugt aus den _OBS_JWT_*-Werten einen Token (HS256) |
| 9 | Misserfolg, der Server antwortet mit 403 und protokolliert den Wert von error |
Fehlerbehandlung im Skript
Tritt im Skript eine Exception auf oder schlägt die Syntax-Prüfung fehl, antwortet der Server mit 500 Interner Fehler und protokolliert die Detail-Meldung in RESTSRV_PROTO (mit Skript-Fehlertext). Der Konsument sieht keine internen Details.
Will das Skript einen spezifischen Fehler an den Konsumenten zurückgeben, ist eine eigene JSON-Struktur zu liefern:
function Post(oParams: TStrings; oBody: TJSONObject): string;
var oRes: TJSONObject;
begin
oRes := TJSONObject.Create();
try
if (Empty(oParams.Values['kundennr'])) then begin
oRes.AddPair('status', 'error');
oRes.AddPair('msg' , 'Parameter ''kundennr'' fehlt');
result := oRes.ToJSON();
exit;
end;
// ...
finally
MyFreeAndNil(oRes);
end;
end;
Skript-Cache
Der Server cached das kompilierte Skript pro Endpunkt (Schlüssel: sys_date des Endpunkts). Zusätzlich werden die Endpunkt-Definitionen pro Server-Profil in einem TTL-Cache (Standard 60 s) gehalten. Eine Skript-Änderung über F7 wird daher erst nach Ablauf dieses TTL (bzw. nach einer Cache-Invalidierung) wirksam - typischerweise innerhalb einer Minute, nicht zwingend sofort.
Verfügbare Bibliotheken
Im Skript können alle OBS-Standard-Bibliotheken verwendet werden. Typische Einstiegspunkte:
- Base.Tools - String-, Datum-, IIF-, Empty-Helper
- Base.DB / Base.xQuery - Datenbank-Operationen, DB_SQLVal, DB_SOpen, DB_LSeek
- Base.qSqlReg - qSqlInit, qSet, SaveData
- System.JSON - JSON-Objekte und -Arrays
- Base.ToolsConst - Konstanten wie CRLF, SINGELQUOTE, EMPTY_DATE
Konkrete Beispiele: