OBS/Kostenpflichtige Module/RESTServer/Beispiel3

Aus OBS Wiki
Version vom 19. Mai 2026, 06:45 Uhr von Rademacker (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „{{Kostenpflichtige Module}} =Beispiel 2: Datensatz anlegen mit JSON-Body= Dieses Beispiel zeigt einen Endpunkt mit allen vier CRUD-Methoden (GET, POST, PUT, DELETE). Es geht um Tickets eines externen Servicedesks, der Tickets ueber den OBS REST-Server in OBS einliefern, abrufen, aktualisieren und schliessen kann. ==Einrichtung in OBS== * '''Server-Profil:''' Standard-TLS-Profil ''Public-API'', Bindung 0.0.0.0:443 * '''Zugang:''' ''Servicedesk-X'' ** A…“)
Zur Navigation springen Zur Suche springen
Kostenpflichtige Module

Internet-Shop
UPS
IMS Professional
SMS
Mehrlager-Verwaltung
Mehrsprachen Modul
Multilanguage Modul
EVA Marketing Tool
Termin-Projekte
Edifact-Schnittstelle
Backup Überwachung Email
OBS Geo Daten
DeliSprint / DPD
Filialen
Cashback
Moebelschnittstelle
Dokumenten Manager
DocuWare-Schnittstelle
OFML-Kalkulation
Versicherungsschaden
Gutschriftsanzeigen
Kameraverwaltung
DataInOut
OpenMasterData / IDS
Sammelpositionen


Beispiel 2: Datensatz anlegen mit JSON-Body

Dieses Beispiel zeigt einen Endpunkt mit allen vier CRUD-Methoden (GET, POST, PUT, DELETE). Es geht um Tickets eines externen Servicedesks, der Tickets ueber den OBS REST-Server in OBS einliefern, abrufen, aktualisieren und schliessen kann.

Einrichtung in OBS

  • Server-Profil: Standard-TLS-Profil Public-API, Bindung 0.0.0.0:443
  • Zugang: Servicedesk-X
    • API-Key: zufaellig generiert
    • Host: servicedesk.kunde.de (DNS-gebunden, IP-Zugriff wird abgelehnt)
    • JWT: nicht aktiv
  • Endpunkt: tickets/v1, Profil Public-API
  • Berechtigung: Zugang Servicedesk-X fuer Endpunkt tickets/v1 freigeschaltet

Endpunkt-Skript tickets/v1

//------------------------------------------------------------------------------
// Hilfsfunktion: einzelnes Ticket als JSON-Objekt aufbauen
//------------------------------------------------------------------------------

function _TicketAsJSON(qTicket: TxFQuery): TJSONObject;
begin
    result := TJSONObject.Create();
    result.AddPair('id'        , qTicket.A2UID());
    result.AddPair('nr'        , qTicket.A2C('ti_nr'));
    result.AddPair('betreff'   , qTicket.A2C('ti_betreff'));
    result.AddPair('status'    , qTicket.A2C('ti_status'));
    result.AddPair('beschr'    , qTicket.A2C('ti_beschr'));
    result.AddPair('aenderung' , DTToSQL(qTicket.A2D('ti_aend_dat')));
end;

//------------------------------------------------------------------------------
// GET   /tickets/v1?id=...   - einzelnes Ticket
// GET   /tickets/v1          - alle offenen Tickets
//------------------------------------------------------------------------------

function Get(oBody: TJSONObject; oParams: TStrings): string;
var cSql  : string;
    qData : TxFQuery;
    oArr  : TJSONArray;
    cId   : string;
begin
    cId := oParams.Values['id'];

    if (not Empty(cId)) then begin
        cSql := 'SELECT * FROM tickets WHERE sys_uid = ' + DB_SQLVal(cId);
        if (DB_SOpen('Y00CXXXXBA', oDB, cSql, qData)) then begin
            result := _TicketAsJSON(qData).ToJSON();
        end else begin
            result := '{"error":"not found"}';
        end;
        DB_Close(qData);
    end else begin
        oArr := TJSONArray.Create();
        try
            cSql := 'SELECT * FROM tickets WHERE ti_status <> "9" ORDER BY ti_aend_dat DESC';
            if (DB_SOpen('Y00CXXXXBB', oDB, cSql, qData)) then begin
                while (not qData.EoF) do begin
                    oArr.Add(_TicketAsJSON(qData));
                    qData.Next();
                end;
            end;
            DB_Close(qData);

            result := oArr.ToJSON();
        finally
            MyFreeAndNil(oArr);
        end;
    end;
end;

//------------------------------------------------------------------------------
// POST  /tickets/v1          - neues Ticket anlegen
// Body: { "betreff":"...", "beschr":"..." }
//------------------------------------------------------------------------------

function Post(oBody: TJSONObject; oParams: TStrings): string;
var xTicket : TqSQL;
    cId     : string;
    cBetreff: string;
    cBeschr : string;
    oRes    : TJSONObject;
begin
    if (not Assigned(oBody)) then begin
        result := '{"error":"JSON-Body erforderlich"}';
        exit;
    end;

    cBetreff := '';
    cBeschr  := '';
    oBody.TryGetValue<string>('betreff', cBetreff);
    oBody.TryGetValue<string>('beschr' , cBeschr);

    if (Empty(cBetreff)) then begin
        result := '{"error":"Feld ''betreff'' fehlt"}';
        exit;
    end;

    cId := GetNewId(oDB);

    xTicket := qSqlInit('Y00CXXXXBC', oDB, 'tickets');
    xTicket.lNoSysUID := True;
    try
        xTicket.qSet('sys_uid'    , cId);
        xTicket.qSet('ti_nr'      , DB_NeuNum(oDB, 'tickets', 'ti_nr', NEUNUM_HOLE, '', '', 1, 999999, '0'));
        xTicket.qSet('ti_betreff' , cBetreff);
        xTicket.qSet('ti_beschr'  , cBeschr);
        xTicket.qSet('ti_status'  , '1');
        xTicket.qSet('ti_anl_dat' , Now());
        xTicket.qSet('ti_aend_dat', Now());
        xTicket.SaveData(NEW_RECORD);
    finally
        qSqlFree(xTicket);
    end;

    oRes := TJSONObject.Create();
    try
        oRes.AddPair('id'    , cId);
        oRes.AddPair('status', 'created');
        result := oRes.ToJSON();
    finally
        MyFreeAndNil(oRes);
    end;
end;

//------------------------------------------------------------------------------
// PUT   /tickets/v1          - Ticket aktualisieren
// Body: { "id":"...", "status":"...", "beschr":"..." }
//------------------------------------------------------------------------------

function Put(oBody: TJSONObject; oParams: TStrings): string;
var xTicket: TqSQL;
    cId    : string;
    cStat  : string;
    cBeschr: string;
begin
    if (not Assigned(oBody)) then begin
        result := '{"error":"JSON-Body erforderlich"}';
        exit;
    end;

    cId     := '';
    cStat   := '';
    cBeschr := '';
    oBody.TryGetValue<string>('id'    , cId);
    oBody.TryGetValue<string>('status', cStat);
    oBody.TryGetValue<string>('beschr', cBeschr);

    if (Empty(cId)) then begin
        result := '{"error":"Feld ''id'' fehlt"}';
        exit;
    end;

    if (not DB_LSeek(oDB, 'tickets', 'sys_uid = ' + DB_SQLVal(cId))) then begin
        result := '{"error":"not found"}';
        exit;
    end;

    xTicket := qSqlRead('Y00CXXXXBD', oDB, 'tickets', 'sys_uid = ' + DB_SQLVal(cId));
    try
        if (not Empty(cStat))   then xTicket.qSet('ti_status'  , cStat);
        if (not Empty(cBeschr)) then xTicket.qSet('ti_beschr'  , cBeschr);
        xTicket.qSet('ti_aend_dat', Now());
        xTicket.SaveData(UPDATE_RECORD);
    finally
        qSqlFree(xTicket);
    end;

    result := '{"status":"updated"}';
end;

//------------------------------------------------------------------------------
// DELETE /tickets/v1?id=...  - Ticket schliessen (Soft-Delete via Status=9)
//------------------------------------------------------------------------------

function Delete(oBody: TJSONObject; oParams: TStrings): string;
var xTicket: TqSQL;
    cId    : string;
begin
    cId := oParams.Values['id'];
    if (Empty(cId)) then begin
        result := '{"error":"Parameter ''id'' fehlt"}';
        exit;
    end;

    if (not DB_LSeek(oDB, 'tickets', 'sys_uid = ' + DB_SQLVal(cId))) then begin
        result := '{"error":"not found"}';
        exit;
    end;

    xTicket := qSqlRead('Y00CXXXXBE', oDB, 'tickets', 'sys_uid = ' + DB_SQLVal(cId));
    try
        xTicket.qSet('ti_status'  , '9');
        xTicket.qSet('ti_aend_dat', Now());
        xTicket.SaveData(UPDATE_RECORD);
    finally
        qSqlFree(xTicket);
    end;

    result := '{"status":"closed"}';
end;

Aufruf mit curl

Liste aller offenen Tickets:

curl -H "apikey: [API-KEY]" https://api.meinserver.de/tickets/v1

Einzelnes Ticket:

curl -H "apikey: [API-KEY]" "https://api.meinserver.de/tickets/v1?id=abc-123"

Neues Ticket anlegen:

curl -X POST -H "apikey: [API-KEY]" -H "Content-Type: application/json" ^
     -d "{\"betreff\":\"Drucker offline\",\"beschr\":\"Etage 2, Raum 23\"}" ^
     https://api.meinserver.de/tickets/v1

Ticket aktualisieren:

curl -X PUT -H "apikey: [API-KEY]" -H "Content-Type: application/json" ^
     -d "{\"id\":\"abc-123\",\"status\":\"2\"}" ^
     https://api.meinserver.de/tickets/v1

Ticket schliessen:

curl -X DELETE -H "apikey: [API-KEY]" "https://api.meinserver.de/tickets/v1?id=abc-123"

Aufruf aus PHP

<?php
$apikey = '[API-KEY]';
$base   = 'https://api.meinserver.de/tickets/v1';

function rest($method, $url, $apikey, $body = null) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL           , $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST , $method);
    $headers = ['apikey: ' . $apikey];
    if ($body !== null) {
        $headers[] = 'Content-Type: application/json';
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
    }
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    $res = curl_exec($ch);
    curl_close($ch);
    return json_decode($res, true);
}

// Neues Ticket anlegen
$neu = rest('POST', $base, $apikey, [
    'betreff' => 'Drucker offline',
    'beschr'  => 'Etage 2, Raum 23'
]);
$id = $neu['id'];

// Status aktualisieren
rest('PUT', $base, $apikey, ['id' => $id, 'status' => '2']);

// Liste auslesen
$alle = rest('GET', $base, $apikey);
foreach ($alle as $t) {
    echo $t['nr'] . ' - ' . $t['betreff'] . PHP_EOL;
}
?>

Was zeigt das Beispiel?

  • Saubere Trennung der CRUD-Methoden in einem Endpunkt.
  • Verwendung des JSON-Body fuer komplexere Eingangsdaten (POST, PUT).
  • Verwendung von Query-Parametern fuer einfache Werte (GET, DELETE).
  • Soft-Delete ueber Status-Aenderung statt physischer Loeschung.
  • Eindeutige Audit-UIDs (Y00CXXXXxx) fuer jeden DB-Zugriff zur Nachvollziehbarkeit.
  • Host-gebundener Zugang als zweite Sicherheitsebene neben dem API-Key.