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

REST API s RemObjects SDK zvyčajne nezlyháva pri „Hello World“ servise, ale presne tam, kde sa pretínajú prevádzka, legacy riešenia a integrácia: verzionovanie bez odstávky, konzistentné správanie pri chybách na všetkých endpointoch, reprodukovateľné ladenie pri proxy-reťazcoch a schopnosť jednoznačne korelovať requesty v prípade problémov.

RemObjects SDK prináša pre tieto scenáre 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é body na centralizované spracovanie chýb. Čo však v rastúcich podnikových softvérových krajinách často chýba, je konzekventne zavedený kontrakt: ktoré polia v JSON sú stabilné? Ako signalizujeme chyby? Ako znova identifikujeme request, keď prešiel Load Balancerom, TLS-termináciou a niekoľkými vrstvami backendu?

Nasledujúci prístup (vrátane Delphi-snippetov) ukazuje robustnú líniu pre RemObjects SDK: versionovanie JSON-kontraktov, vynucovanie Correlation-ID (Request-ID na sledovanie), prekladanie Exceptions do HTTP-statusov a JSON-chybových objektov a pritom nesystémové protirečenie medzi ladením a prevádzkou. Okrem toho sa pozrieme na okrajové prípady, ktoré sa v reálnych prostrediach pravidelne vyskytujú: threading na serveri, prístupy do databázy pri BDE-odstraňovaní s natívnym pripojením, proxy-headery, time-outy a „špinavé“ klientské payloady.

Architektonické rozhodnutie: verzionovanie cez Medien-Typ namiesto URL

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

Alternatívou je verzionovanie cez Media Type (Content Negotiation). Klient pošle napr. Accept: application/vnd.company.order+json;v=2. Server deterministicky prečíta verziu a upraví správanie kontraktu/DTO. Funguje to v proxy- a cache-reťazcoch, ak sa hlavičky preposielajú bez úprav. Pre administrátorov je to tiež dobre overiteľné: request sa dá reprodukovať cez Curl/Postman bez rozdielu v URL.

RemObjects SDK nie je „REST-puristické“, ale pragmatické servisné framework. Práve preto má zmysel varianta s Media Type: môžete zachovať stabilné endpointy a zároveň vyvíjať kontrakty ďalej. Dôležité je vždy vyhodnocovať verziu na jednom centrálnom mieste a výsledok previesť do kontextu služby.

Kedy zlyhá varianta s Accept-Headerom?

V praxi existujú tri typické body zlomu, ktoré je potrebné predom adresovať:

  • Proxy-Policies: Niektoré reverse proxy alebo pravidlá WAF normalizujú alebo filtrujú Accept-header. Vaše API potom ticho padne na default. Riešenie: explicitne skontrolovať pravidlá proxy, prípadne sa uchýliť k X-Api-Version ako fallback.
  • Client-Libraries: Niektoré HTTP-knižnice nastavujú vlastné Accept-headery a prepíšu hodnoty. Riešenie: podporiť verziu kontraktu tiež ako nepovinný query-parameter (len ako fallback) alebo serverovo tolerovať a robustne parsovať Accept-header.
  • Caching: Ak sa používa Response-Caching, musí sa cache líšiť podľa hlavičky Accept (Vary: Accept), inak doručí verziu 1 klientom verzie 2. Riešenie: vedome nastaviť Vary alebo deaktivovať cachovanie na úrovni API.
  • Ukážka zdrojového kódu: Request-Context, Correlation-ID, verzia a Error-Mapping

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

    • Correlation-ID: Jedinečné ID pre každý Request, ktoré sa vráti v odpovedi a na ktoré sa odkazuje v logoch.
    • Exception-Mapping: Preklad interných Delphi-Exceptions do stabilných, klientom spracovateľných objektov 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;
    // Erwartet z.B.: 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;
    // Im Betrieb keine internen Details, keine SQL, keine Pfade.
    // Für Debug/Stage kann man das über Konfiguration erweitern.
    begin
      if E is EApiError then
        Exit(E.Message);
    
      if E is EArgumentException then
        Exit('Ungültige Parameter.');
    
      Exit('Interner Fehler.');
    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 „irgendwo im Threadlocal“

    Útržok rozdeľuje zámerne: TApiContext je minimálny stav, ktorý chcete odovzdávať. V RemObjects SDK veľa beží cez server-/channel-kontext. V heterogénnych projektoch (napr. dodatočné worker-thready, DB-queue, background joby) je explicitné odovzdávanie často robustnejšie než implicitné Threadlocals, pretože tak robíte súbežnosť a zmeny kontextu viditeľnejšími.

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

    Úskalia: Verzionovanie cez Accept je len také dobré, ako sú vaše testy. Ak klienti používajú knižnice, ktoré Accept prepíšu, API môže náhle spadnúť na predvolenú verziu. Pre legacy klietov je predvolený fallback rozumný, ale musí byť viditeľný v monitoringu (napr. log-warning „Version defaulted“).

    Varianty: Ak preferujete verzovanie cez X-Api-Version: parser je identický, len je zdroj iná hlavička. Z pohľadu gatewayov je to niekedy jednoduchšie kontrolovať.

    Integrácia do RemObjects SDK: Correlation-ID a Exception-Mapping pri vstupe do servisu

    Skutočný efekt vznikne, keď mechaniku konzistentne použijete na okraji vášho servera: raz pri vstupe requestu čítať z hlavičiek, raz pri výstupe z výnimky mapovať do stabilnej response. Podľa hostingu (napr. RO-HTTP-Server, IIS-Hosting, vlastné Windows-/Windows- a Linux-services) sa konkrétne hook-pointy líšia; princíp je 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 na začiatku škáluje dobre, no pri prevádzke to kolabuje: každá metóda buduje logging a spracovanie chýb inak. Čisté oddelenie je základ pre služby alebo dispatcher, ktorý štandardizuje správanie.

    Praktický postup (vedome stručný a implementačne orientovaný)

    1. Prečítať Correlation-ID z request-hlavičky X-Correlation-ID; ak chýba, vygenerovať server-side (napr. GUID).
    2. Prečítať contract-verziu z Accept (alebo z X-Api-Version).
    3. Zalogovať začiatok requestu: metóda, cesta, Correlation-ID, remote IP, spustiť meranie trvania.
    4. Spustiť business logiku; DB-prístupy kde možno zapuzdriť transakčne.
    5. Zachytiť Exception: 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.

    Threading na serveri: Prečo je Correlation-ID bez kontextovej disciplíny bezcenná

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

    Pragmatické pravidlo: Všetko, čo neostáva striktne v request-threade, dostane kontext explicitne odovzdaný. Aj keď to vyzerá ako dlhšie zoznamy parametrov, vyplatí sa to. Alternatívne môžete použiť jasne definovaný kontext-objekt, ktorý zámerne odovzdáte workerom (namiesto globálnych premenných alebo skrytých singletonov).

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

    • DB-Connections na vlákno: BDE-Ablosung mit nativer Anbindung-Verbindungen sind nicht automatisch thread-sicher teilbar. Ein Connection-Pool oder pro Thread eine Verbindung ist häufig sinnvoller als „eine globale Connection“.
    • Transaktionsgrenzen: Ak máte v rámci jednej požiadavky niekoľko krokov, ktoré k sebe patria, musí transakcia zostať v tej istej logickej jednotke. Asynchrónna práca nesmie „náhodou“ pokračovať v tej istej transakcii.
    • Cancellation: Keď klient preruší (Proxy timeout, Browser closed), server často beží ďalej. Zvážte vedome, či má pozadová práca v takom prípade ešte zmysel.

    Prístup k dátam a chybové kódy: 409 ist nicht „auch ein 500“

    V integračných projektoch je čisté mapovanie chýb viac než kozmetika. Rozhoduje, či protistrana (ERP-Connector, ETL-Job, zákaznícky portál) dokáže korektne reagovať. Niekoľko praktických pravidiel, 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 keď je telo poškodené.
    • 401/403: Autentifikáciu a autorizáciu oddeľte. 401 znamená „žiadna/neplatná identita“, 403 „identita OK, ale zakázané“.
    • 404: Zdroj neexistuje. Pozor pri bezpečnosti: Nie je vždy vhodné prezradiť, či niečo existuje.
    • 409 Conflict: Doménny konflikt (napr. konflikt verzií, „Status erlaubt diese Aktion nicht“, porušenie unikátneho kľúča, ak je to významné z pohľadu domény).
    • 422 Unprocessable Content: Keď je syntaktika v poriadku, ale zlyhá doménová validácia (nie každý tím používa 422, no často je to jasnejšie než 400).
    • 500: Všetko, čo nedokážete presne zaradiť. Patrí sem aj „DB down“, „Timeout“, „Unhandled Exception“.

    Delphi-špecifický trik: Mnohé DB‑chyby sa zobrazujú ako generické výnimky. Oplatí sa v dátovej vrstve cielene kontrolovať známe situácie a previesť ich do EApiError. Dôležité: Neprenášajte do správy klientovi SQL‑fragmenty ani interné názvy tabuliek/stĺpcov. Tieto detaily patria do logu, nie do response.

    Debugging‑Trick: reprodukovateľné chyby cez „Contract Snapshot“

    Neobvyklé, ale v prevádzke mimoriadne užitočné: Pri chybách (alebo cielene pre určité Correlation‑IDs) uložte „Snapshot“ z Request‑Headern + Request‑Body do debug‑spool súboru. Nie je to trvalé logovanie (ochrana údajov/objem), ale kontrolovaný nástroj na nahrávanie ťažko reprodukovateľných prípadov z blízkosti produkcie.

    Dôležité: Snapshot nesmie nikdy nepretržite ukladať autentifikačné hlavičky, 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‑IDs, krátke časové okná).

    Čisté prevedenie v praxi: Maskovanie namiesto vynechávania

    V reálnych integráciách sú práve „kritické“ polia často tie, ktoré by ste pri debugovaní potrebovali (napr. identifikátory). Namiesto všeobecného vymazávania je lepšie maskovať: čiastočne nahradiť tokeny, ponechať e‑mail len s doménou, IBAN len s poslednými číslicami. Tak zostane prípad reprodukovateľný bez zbytočného šírenia údajov v súborovom systéme. Navyše by mal byť Snapshot jasne označený ako debug‑artefakt a mať definovanú dobu uchovania.

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

    REST API zriedka končí priamo na kliente. Typické sú reťazce z reverse proxy, TLS-terminácie, WAF alebo API-gateway. Z toho vyplývajú praktické body:

    • Remote IP: Nespoliehajte sa slepo na X-Forwarded-For. Akceptujte ho iba od dôveryhodných proxy a inak použite priamu socket-IP. V prevádzkových príručkách musí byť uvedené, ktoré hop-y sú „trusted“.
    • Timeouts: Ak má proxy 30 sekúnd, ale vaše backendy potrebujú 2 minúty, vytvárate falošné požiadavky. Nastavte time-outy konzistentne naprieč reťazcom a rozhodnite sa: synchroný request alebo job-pattern (202 Accepted + status-endpoint).
    • Correlation-ID: Umiestnite Correlation-ID aj do response-hlavičiek, aby administrátori mohli spojiť záznamy z logov a klientskú stranu. Ak gateway používa vlastné Request-ID: logujte a mapujte obe ID.
    • Fehlertexte: V produkčnej prevádzke žiadne interné detaily. Debug-detalje len kontrolovane (Stage/Feature-Flag) a v prípade pochybností iba v logu.

    Zaradenie: Prečo môže mať RemObjects SDK tu výhodu

    V Delphi-ekosystémoch sa REST-servery často stavajú s ľahšími frameworkmi (napr. minimalistické HTTP-routery). RemObjects SDK ukáže svoje silné stránky, keď už máte alebo potrebujete viacvrstvovú architektúru:

    • Jasné hranice služieb: Service-metódy sú explicitné, kontrakty sú verzionovateľné.
    • Transporty a serializácia: Môžete komunikovať v JSON, ale aj inými message-formátmi (podľa nastavenia), bez premiešania doménovej logiky.
    • Prevádzka: Hostingové možnosti a integrácia do existujúcich Windows- a Linux-servicov sú plánovateľné, vrátane čistých roll-outov.

    Ukázaný prístup dopĺňa časti, ktoré v každodennej praxi často chýbajú: jednotné chybové objekty, deterministické verzionovanie a korelovateľné logovanie. Najmä pri individuálnom firemnom softvéri s dlhými životnými cyklami to šetrí čas pri aktualizáciách a pri integrácii externých systémov.

    Záver: Stojí to za námahu — a kde sa prístup prekvalifikuje?

    Pridaná hodnota vzniká, keď vaše REST rozhranie nielen „funguje“, ale je trvalo prevádzkyschopné: stabilné JSON-kontrakty, verzionovanie bez nekontrolovaného množstva URL, vysledovateľné chyby a debugovanie bez hádania. Práve tu je prístup s Context, Correlation-ID a centrálnym mapovaním výnimiek v RemObjects SDK silný.

    Hraničné podmienky použitia: Ak máte len jediný, krátkodobý endpoint bez integračných partnerov, Media-Type-Versionierung rýchlo pôsobí ako overengineering. Aj snapshot-logging dáva zmysel len ak disciplinovane implementujete redaction a aktiváciu. A: ak váš proxy-stack hlavičky „optimalizuje“ alebo odstraňuje, musíte najprv upraviť infraštruktúru, inak budete debugovať nesprávnu vrstvu.

    Ak modernizujete existujúcu Delphi serverovú krajinu alebo potrebujete procesne-približnú softvérovú riešenie čisto integrovať do ERP/DMS/CRM, práve tieto mechanizmy často rozhodujú o tom, či systém „beží v teste“ alebo „beží v prevádzke“.

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

    Prediskutovať projekt alebo zámer modernizácie s Net-Base.

    ďalší krok

    Keď sa z témy stane reálny projekt, 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 údajom, portály a nasadenie nebudú odložené na neskôr ako následné úlohy.
    • Včas identifikujete, 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ú okamžite k dispozícii. Pre Instagram pripravíme priamo odkaz a stručný text.

    E-mail

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