Net-Base Magazín

09.06.2026

REST API s RemObjects SDK: JSON koncové body — konzistentné verzionovanie a ladenie (Delphi ukážky zdrojového kódu)

Ako pomocou RemObjects SDK v Delphi vybudujete REST API, ktorá v prevádzke nezlyhá: stabilné JSON kontrakty, verzionovanie bez neprehľadného množstva URL, Correlation-ID naprieč všetkými vrstvami, centrálne mapovanie chýb, snapshot-logging pre náročné prípady ladenia a praktické odporúčania...

09.06.2026

Od témy magazínu k projektovej praxi

Súvisiace stránky služieb a technológií k príspevku

Prečo „REST API s RemObjects SDK“ v praxi často rozhoduje na okrajoch

Jedna REST API s RemObjects SDK zriedka stojí alebo padá na „Hello World“ servise, ale na miestach, kde sa prevádzka, legacy a integrácia stretávajú: verzionovanie bez odstávky, konzistentné správanie pri chybách naprieč všetkými koncovými bodmi, reprodukovateľné ladenie pri proxy reťazcoch a schopnosť jednoznačne korelovať požiadavky v prípade problémov.

RemObjects SDK prináša veľa infraštruktúry: služby, formáty správ, serializáciu, hosting (napr. ako Windows- a Linux-Services alebo za IIS/Reverse Proxy) a definované miesta na centrálne spracovanie chýb. Čo však v zabehnutých podnikových softvérových prostrediach často chýba, je konzekventne uplatňovaná zmluva: Ktoré JSON-políčka sú stabilné? Ako signalizujeme chyby? Ako opätovne identifikujeme požiadavku, keď prešla cez load balancer, TLS-termináciu a viacero backend vrstiev?

Nasledujúci prístup (vrátane Delphi-snippetu) ukazuje robustnú líniu pre RemObjects SDK: verzionovať JSON-zmluvy, vynucovať Correlation-ID (Request-ID na sledovanie), prekladať výnimky do HTTP statusov a JSON-chybových objektov a pritom nenechať ladenie a prevádzku hrať proti sebe. Navyše sa pozrieme na okrajové prípady, ktoré v reálnych prostrediach pravidelne nastávajú: spracovanie vo viacerých vláknach na serveri, prístupy do databázy pri BDE-náhrade s natívnym napojením, proxy-headery, timeouts a „špinavé“ klientské payloady.

Rozhodnutie v architektúre: verzionovanie cez Media Type namiesto URL

Mnohé API verzionujú cez cesty ako /v1/. Je to pragmatické, ale v dlhšie bežiacich integráciách (napr. pripojenia ERP/DMS/CRM) to často vedie k duplikácii URL, duplicitným routám, duplicitným testom a otázke „Ktorú verziu vlastne používame?“ v prevádzkových príručkách.

Alternatívou je verzionovanie cez Media Type (content negotiation). Klient napr. pošle Accept: application/vnd.company.order+json;v=2. Server deterministicky načíta verziu a prispôsobí správanie kontraktu/DTO. Funguje to v proxy a cache reťazcoch, ak sú headery korektne posielané ďalej. Pre adminov je to tiež ľahko overiteľné: požiadavku je možné reprodukovať cez Curl/Postman bez zmeny URL.

RemObjects SDK nie je „REST-puristické“, ale pragmatické service-framework. Práve preto má varianta s media-typom zmysel: môžete si ponechať stabilné endpointy a napriek tomu vyvíjať zmluvy ďalej. Dôležité je, aby ste verziu vždy vyhodnocovali, rozhodovali centrálne na jednom mieste a výsledok previedli do kontextu služby.

Kedy zlyháva varianta s Accept-headerom?

V praxi existujú tri typické slabé miesta, ktoré by ste mali predom adresovať:

  • Proxy-politiky: Niektoré reverse proxy/WAF pravidlá normalizujú alebo filtrujú Accept-header. V takom prípade vaša API ticho padne na predvolenú verziu. Riešenie: explicitne skontrolovať pravidlá proxy, prípadne použiť X-Api-Version ako zálohu.
  • Knižnice klienta: Niektoré HTTP-klienty nastavia vlastné Accept-headery a prepíšu hodnoty. Riešenie: podporiť verziu kontraktu aj ako voliteľný query-parameter (len ako fallback), alebo Accept-header na serveri tolerantne parsovať.
  • Kešovanie: Ak je v hre kešovanie odpovedí, musí sa cache variovať podľa Accept (Vary: Accept), inak bude doručená verzia 1 klientom verzie 2. Riešenie: explicitne nastaviť Vary alebo zakázať kešovanie na úrovni API.
  • Ukážka zdrojového kódu: Request-Context, Correlation-ID, verzia a mapovanie chýb

    Kód je zámerne zostavený tak, aby sa dal integrovať do existujúcich RemObjects-serverprojektov: malá vrstva contextu, parser pre API-verziu (z Accept), mechanizmus Correlation-ID a centrálne Exception-Mapping. Pojmy:

    • Correlation-ID: Jedinečné ID pre požiadavku, ktoré sa objaví v odpovedi a slúži ako referencia v logoch.
    • Exception-Mapping: Preklad interných Delphi-Exceptions na stabilné, klientom spracovateľné objekty chýb (vrátane HTTP-Status).
    • Contract-Version: Verzia JSON kontraktu, ktorá riadi správanie a polia.
    Delphi
    unit Api.Infrastructure;
    
    interface
    
    uses
      System.SysUtils, System.Classes, System.StrUtils, System.Generics.Collections,
      System.JSON;
    
    type
      EApiError = class(Exception)
      private
        FHttpStatus: Integer;
        FCode: string;
        FCorrelationId: string;
      public
        constructor Create(const AHttpStatus: Integer; const ACode, AMessage, ACorrelationId: string);
        property HttpStatus: Integer read FHttpStatus;
        property Code: string read FCode;
        property CorrelationId: string read FCorrelationId;
      end;
    
      TApiContext = record
        CorrelationId: string;
        ContractVersion: Integer;
        RemoteIp: string;
        UserAgent: string;
        class function New: TApiContext; static;
      end;
    
      TApiVersion = record
        class function FromAcceptHeader(const AAccept: string; const ADefault: Integer = 1): Integer; static;
      end;
    
      TApiErrorMapper = class
      public
        class function ToErrorJson(const E: Exception; const ACorrId: string): TJSONObject; static;
        class function ToHttpStatus(const E: Exception): Integer; static;
        class function SafeMessage(const E: Exception): string; static;
      end;
    
    implementation
    
    { EApiError }
    
    constructor EApiError.Create(const AHttpStatus: Integer; const ACode, AMessage, ACorrelationId: string);
    begin
      inherited Create(AMessage);
      FHttpStatus := AHttpStatus;
      FCode := ACode;
      FCorrelationId := ACorrelationId;
    end;
    
    { TApiContext }
    
    class function TApiContext.New: TApiContext;
    begin
      Result.CorrelationId := '';
      Result.ContractVersion := 1;
      Result.RemoteIp := '';
      Result.UserAgent := '';
    end;
    
    { TApiVersion }
    
    class function TApiVersion.FromAcceptHeader(const AAccept: string; const ADefault: Integer): Integer;
    // Očakáva napr.: application/vnd.company.order+json;v=2
    var
      Parts: TArray<string>;
      P: string;
      V: string;
      I: Integer;
    begin
      Result := ADefault;
      if AAccept.Trim.IsEmpty then
        Exit;
    
      Parts := AAccept.Split([';', ',']);
      for P in Parts do
      begin
        V := Trim(P);
        if StartsText('v=', V) then
        begin
          if TryStrToInt(Copy(V, 3, MaxInt), I) and (I > 0) and (I < 100) then
            Exit(I);
        end;
      end;
    end;
    
    { TApiErrorMapper }
    
    class function TApiErrorMapper.SafeMessage(const E: Exception): string;
    // V prevádzke žiadne interné detaily, žiadne SQL, žiadne cesty.
    // Pre Debug/Stage je možné to rozšíriť cez konfiguráciu.
    begin
      if E is EApiError then
        Exit(E.Message);
    
      if E is EArgumentException then
        Exit('Neplatné parametre.');
    
      Exit('Interná chyba.');
    end;
    
    class function TApiErrorMapper.ToHttpStatus(const E: Exception): Integer;
    begin
      if E is EApiError then
        Exit(EApiError(E).HttpStatus);
    
      if E is EArgumentException then
        Exit(400);
    
      Exit(500);
    end;
    
    class function TApiErrorMapper.ToErrorJson(const E: Exception; const ACorrId: string): TJSONObject;
    var
      Code: string;
      Status: Integer;
      Msg: string;
    begin
      Status := ToHttpStatus(E);
      Msg := SafeMessage(E);
    
      if E is EApiError then
        Code := EApiError(E).Code
      else if E is EArgumentException then
        Code := 'bad_request'
      else
        Code := 'internal_error';
    
      Result := TJSONObject.Create;
      Result.AddPair('error', TJSONObject.Create
        .AddPair('code', Code)
        .AddPair('message', Msg)
        .AddPair('httpStatus', TJSONNumber.Create(Status))
        .AddPair('correlationId', ACorrId));
    end;
    
    end.

    Účel: stabilný kontext požiadavky namiesto „niekde v Threadlocal“

    Útržok kódu vedome rozdeľuje: TApiContext je minimálny stav, ktorý chcete prenášať. V RemObjects SDK prebieha veľa cez server-/channel-kontext. V heterogénnych projektoch (napr. dodatočné pracovné vlákna, DB fronta, úlohy na pozadí) je explicitné prenášanie často robustnejšie než implicitné threadlocaly, pretože tým robíte súbežnosť a prepínanie kontextu viditeľnejšími.

    Predpoklady: Varianta s Accept-Header predpokladá, že váš reverse proxy (nginx, IIS ARR, Traefik) preposiela hlavičku nezmenenú. V niektorých prostrediach sú „neobvyklé“ Accept-Header filtrované alebo zjednocované.

    Úskalia: Verzionovanie cez Accept je také dobré, ako vaše testy. Ak klienti používajú knižnice, ktoré prepíšu Accept, môže API náhle prepadnúť na predvolené správanie. Pre legacy klientov má predvolený fallback zmysel, ale musí byť viditeľný v monitorovaní (napr. log-varovanie „Version defaulted“).

    Varianty: Ak chcete verzovanie radšej cez X-Api-Version: parser je identický, len zdroj je iný header. Z pohľadu gatewayov je to niekedy ľahšie kontrolovateľné.

    Integrácia v RemObjects SDK: Correlation-ID a mapovanie výnimiek pri vstupe do služby

    Skutočný efekt nastáva, keď mechaniku konzekventne aplikujete na okraji vášho servera: raz pri vstupe požiadavky čítať z hlavičiek, raz pri výstupe výnimiek preložiť do stabilnej response. V závislosti od hostingu (napr. RO-HTTP-Server, IIS-hosting, vlastne prevádzkovaný Windows-/Windows- und Linux-Services) sa konkrétne hook-body líšia; princíp zostáva rovnaký: postaviť context, zavolať business logiku, výnimky centrálne namapovať.

    V RemObjects-projektoch sa často pracuje priamo na úrovni každej service-metódy. To spočiatku dobre škáluje, no pri prevádzke sa to rozpadá: každá metóda implementuje logging a spracovanie chýb inak. Čistý rez je service-báza alebo dispatcher, ktorý štandardizuje.

    Praktický postup (vedome stručný a implementačne blízky)

    1. Prečítať Correlation-ID z Request-Header X-Correlation-ID; ak chýba, vygenerovať na serveri (napr. GUID).
    2. Prečítať verziu kontraktu z Accept (alebo z X-Api-Version).
    3. Zalogovať štart requestu: metóda, cesta, Correlation-ID, remote IP, spustiť meranie trvania.
    4. Spustiť business logiku; prístupy do DB čo najviac zabaliť transakčne.
    5. Ochytiť výnimku: určiť HTTP status, vytvoriť JSON-chybový objekt, nastaviť response-header X-Correlation-ID.
    6. Zalogovať koniec requestu: status, trvanie, prípadne chybový kód.

    Vlákna v serveri: prečo je Correlation-ID bez disciplíny správy kontextu bezcenná

    Častý Delphi-okrajový prípad: service-metóda spustí asynchrónnu prácu (napr. generovanie reportu, import, push do DMS). Potom už pôvodné request-vlákno nie je to, ktoré neskôr zapisuje logy. Ak je Correlation-ID známa len „na začiatku“, sledovateľnosť sa rozpadne.

    Pragmatické pravidlo: Všetko, čo striktne nezostáva v request-vlákne, dostane kontext explicitne odovzdaný. Aj keď to vyzerá ako dlhšie zoznamy parametrov, oplatí sa to. Alternatívou je pracovať s jasne definovaným kontextovým objektom, ktorý sa vedome odovzdáva workerom (namiesto globálnych premenných alebo skrytých singletonov).

    Typické body zlomu v RemObjects-/Delphi-serveroch:

    • DB-pripojenia na vlákno: BDE-Ablosung mit nativer Anbindung-pripojenia nie sú automaticky bezpečne zdieľateľné medzi vláknami. Connection-Pool alebo pre každé vlákno vlastné pripojenie sú často rozumnejšie než „jedno globálne pripojenie“.
    • Hranice transakcií: Ak máte v rámci jedného requestu viacero krokov, ktoré k sebe patria, transakcia musí zostať v tej istej logickej jednotke. Asynchrónna práca nesmie „náhodou“ pokračovať v rámci tej istej transakcie.
    • Zrušenie: Ak klient preruší spojenie (Proxy timeout, Browser closed), server často pokračuje v spracovaní. Dôkladne zvážte, či má v takom prípade pozadová práca zmysel.

    Prístup k dátam a kódy chýb: 409 nie je „tiež 500“

    V integračných projektoch je korektné mapovanie chýb viac než kozmetika. Rozhoduje o tom, či protistrana (ERP-Connector, ETL-Job, zákaznícky portál) dokáže správne reagovať. Niekoľko praktických zásad, ktoré sa osvedčili v Delphi/RemObjects prostrediach:

    • 400 Bad Request: Validácia, chýbajúce/neplatné parametre, JSON neparsovateľný. Dôležité: odpoveď má zostať stabilná aj v prípade poškodeného tela.
    • 401/403: Rozdeľte autentifikáciu a autorizáciu. 401 znamená „žiadna/neplatná identita“, 403 „identita OK, ale zakázané“.
    • 404: Zdroj neexistuje. Pozor na bezpečnosť: nie vždy treba prezradiť, či niečo existuje.
    • 409 Conflict: Odborný konflikt (napr. konflikt verzií, „stav neumožňuje túto akciu“, porušenie unikátneho kľúča, ak má funkčný význam).
    • 422 Unprocessable Content: Keď je syntakticky všetko v poriadku, ale odborná validácia zlyhá (nie každý tím používa 422, no často je výstižnejší ako 400).
    • 500: Všetko, čo nedokážete presne klasifikovať. Sem patrí aj „DB down“, „Timeout“, „Unhandled Exception“.

    Delphi-špecifická rada: Mnohé DB chyby vystupujú ako generické výnimky. Oplatí sa v dátovom prístupovom obale cielene detegovať známe situácie a previesť ich do EApiError. Dôležité pri tom: neprenášajte do správy pre klienta žiadne SQL fragmenty ani interné názvy tabuliek/stĺpcov. Tieto detaily patria do logu, nie do response.

    Trik na ladenie: reprodukovateľné chyby pomocou „Contract Snapshot“

    Neštandardné, ale v prevádzke mimoriadne užitočné: pri chybách (alebo cielene pre určité Correlation‑ID) uložte „snapshot“ z hlavičiek požiadavky + tela požiadavky do debug‑spool súboru. Nie je to trvalé logovanie (ochrana údajov/objem), ale kontrolovaný nástroj na reprodukciu ťažko opakovateľných prípadov z produkčného prostredia.

    Dôležité: snapshot nesmie nikdy neodfiltrovaný persistovať Auth‑header, tokeny alebo osobné údaje. V praxi to znamená: redaction (maskovanie) a aktivácia len cez feature‑flag alebo whitelist (napr. len pre konkrétne Correlation‑ID, v krátkom časovom okne).

    Čisté nasadenie v praxi: maskovanie namiesto vynechávania

    V reálnych integráciách sú práve „kritické“ polia často tie, ktoré potrebujete na ladenie (napr. identifikátory). Namiesto všeobecného vynechávania je lepšie maskovanie: čiastočné nahradenie tokenov, ponechanie len domény v e‑maile, IBAN len s poslednými ciframi. Tak zostane prípad reprodukovateľný bez zbytočného rozptyľovania citlivých údajov v súborovom systéme. Snapshot by mal byť jednoznačne označený ako debug‑artefakt a mať definovanú dobu uchovania.

    Bezpečnosť a prevádzka: odovzdávanie hlavičiek, proxy reťazce a timeouty

    Eine REST API endet selten direkt am Client. Typisch sind Ketten aus Reverse Proxy, TLS-Termination, WAF oder API-Gateway. Daraus ergeben sich praktische Punkte:

    • Remote IP: Verlassen Sie sich nicht blind auf X-Forwarded-For. Nur aus vertrauenswürdigen Proxies übernehmen und sonst die direkte Socket-IP nutzen. In Betriebshandbüchern sollte stehen, welche Hops „trusted“ sind.
    • Timeouts: Wenn Proxy 30 Sekunden hat, Ihr Backend aber 2 Minuten braucht, erzeugen Sie Ghost-Requests. Legen Sie Timeouts entlang der Kette konsistent fest und entscheiden Sie: synchroner Request oder Job-Pattern (202 Accepted + Status-Endpunkt).
    • Correlation-ID: Setzen Sie die Correlation-ID in Response-Headern, damit Admins sie aus Logs und Client-Seite zusammenführen können. Wenn ein Gateway eigene Request-IDs nutzt: beide IDs loggen und abbilden.
    • Fehlertexte: Im Produktivbetrieb keine internen Details. Debug-Details nur kontrolliert (Stage/Feature-Flag) und im Zweifel nur im Log.

    Einordnung: Warum RemObjects SDK hier im Vorteil sein kann

    In Delphi-Ökosystemen werden REST-Server oft mit leichteren Frameworks (z. B. minimalistische HTTP-Router) gebaut. RemObjects SDK spielt seine Stärke aus, wenn Sie bereits eine mehrschichtige Architektur haben oder brauchen:

    • Klare Service-Grenzen: Service-Methoden sind explizit, Contracts sind versionierbar.
    • Transporte und Serialisierung: Sie können JSON sprechen, aber auch andere Message-Formate (je nach Setup), ohne die Fachlogik zu verquirlen.
    • Betrieb: Hosting-Optionen und Integration in bestehende Windows- und Linux-Services sind planbar, inklusive sauberer Rollouts.

    Der gezeigte Ansatz ergänzt das um die Teile, die im Alltag oft fehlen: einheitliche Fehlerobjekte, deterministische Versionierung und korrelierbares Logging. Gerade bei individueller Unternehmenssoftware mit langen Lebenszyklen sparen Sie damit Zeit bei Updates und bei der Integration externer Systeme.

    Fazit: Lohnt sich der Aufwand – und wo kippt der Ansatz?

    Der Mehrwert entsteht, wenn Ihre REST-Schnittstelle nicht nur „funktioniert“, sondern dauerhaft betreibbar ist: stabile JSON-Verträge, Versionierung ohne URL-Wildwuchs, nachvollziehbare Fehler und Debugging ohne Ratespiel. Genau dort ist der Ansatz mit Context, Correlation-ID und zentralem Exception-Mapping in RemObjects SDK stark.

    Einsatzgrenzen: Wenn Sie nur einen einzelnen, kurzlebigen Endpunkt ohne Integrationspartner haben, wirkt Media-Type-Versionierung schnell wie Overengineering. Auch Snapshot-Logging ist nur sinnvoll, wenn Sie Redaction und Aktivierung diszipliniert implementieren. Und: Wenn Ihr Proxy-Stack Header „optimiert“ oder entfernt, müssen Sie zuerst die Infrastruktur geradeziehen, sonst debuggen Sie die falsche Schicht.

    Wenn Sie eine bestehende Delphi-Serverlandschaft modernisieren oder eine prozessnahe Softwarelösung sauber in ERP/DMS/CRM integrieren müssen, sind genau diese Mechanismen aber häufig der Unterschied zwischen „läuft im Test“ und „läuft im Betrieb“.

    V odbornom prostredí zohrávajú tiež Delphi REST-API a REST-Server a Remobjects Sdk Delphi dôležitú úlohu, keď musia integrácie, dátové toky a ďalší vývoj bezchybne spolupracovať.

    Projekt alebo modernizačný zámer prediskutovať s Net-Base.

    Ďalší krok

    Keď sa téma stane reálnym projektom, architektúru, existujúci stav a prevádzku treba včas posudzovať spoločne.

    Podporujeme nielen pri jednotlivých otázkach, ale aj vtedy, keď sa z fragmentov zdrojového kódu, tém súvisiacich s legacy systémami alebo nápadov na portál má stať robustný podnikový projekt.

    • Stav, cieľový obraz a technické riziká sa hodnotia spoločne.
    • REST, prístup k dátam, portály a Rollout nebudú odložené na neskôr.
    • Včas zistíte, ktorá cesta je ekonomicky a prevádzkovo životaschopná.

    Zdieľať príspevok

    Tento príspevok priamo zdieľať

    LinkedIn, X, XING, Facebook, WhatsApp a e-mail sú ihneď k dispozícii. Pre Instagram pripravujeme priamo odkaz a krátky text.

    E-mail

    Instagram sa otvorí v novej karte. Odkaz a krátky text sa predtým skopírujú do schránky.