Net-Base Magazin

09.06.2026

REST API a RemObjects SDK-val: JSON-végpontok tiszta verziózása és hibakeresése (Delphi forrásrészlet)

Hogyan építsen RemObjects SDK-val a Delphi-ban egy REST API-t, amely üzemeltetés közben nem omlik össze: stabil JSON-kontraktusok, verziókezelés URL-ek elszaporodása nélkül, Correlation-ID minden rétegen át, központi hibatérképezés, Snapshot-logging súlyos hibakeresési esetekhez, valamint gyakorlatorientált útmutatók...

09.06.2026

A magazintémától a projektgyakorlatig

A bejegyzéshez tartozó szolgáltatási és technikai oldalak

Miért dől el a gyakorlatban gyakran a „REST API RemObjects SDK-vel” az apró részleteken

Egy REST API RemObjects SDK-vel ritkán múlik a „Hello World”-szolgáltatáson; a döntő pontok ott vannak, ahol az üzemeltetés, a legacy és az integráció találkozik: verziózás megszakítás nélkül, konzisztens hibakezelés minden végponton, reprodukálható hibakeresés proxy-láncok esetén és a képesség, hogy problémás esetekben a kéréseket egyértelműen korrelálni tudjuk.

A RemObjects SDK sok infrastruktúrát hoz a megoldáshoz: szolgáltatások, üzenetformátumok, szerializálás, hosting (pl. Windows és Linux szolgáltatások vagy IIS/Reverse Proxy mögötti üzemeltetés) és meghatározott pontok a hibakezelés centralizálásához. Ami a felhalmozódott üzleti szoftverlandscape-ekben gyakran hiányzik, az egy következetesen végigvitt szerződés: mely JSON-mezők stabilak? Hogyan jelezzük a hibákat? Hogyan azonosítunk vissza egy requestet, ha az Load Balancer-en, TLS-termináción és több backend rétegen átment?

Az alábbi megközelítés (beleértve a Delphi-kódrészletet) egy robusztus irányt mutat a RemObjects SDK-hoz: JSON-szerződések verziózása, Correlation-ID (kérésazonosító a nyomon követéshez) kötelezővé tétele, kivételek lefordítása HTTP-státuszokra és JSON hibajobjektumokra, miközben a hibakeresést és az üzemeltetést nem szembeállítva kezeljük. Emellett foglalkozunk a valós környezetben rendszeresen előforduló szélsőesetekkel: szerveroldali szálkezelés, adatbázis-hozzáférések BDE-kiváltása natív csatlakozással, proxy-fejlécek, time-outok és „piszkos” kliens-payloadok.

Architekturális döntés: verziózás média-típus alapján az URL helyett

Sok API a /v1/ típusú útvonalakkal verzióz. Ez pragmatikus, de hosszabb távú integrációkban (pl. ERP/DMS/CRM csatolások) gyakran URL-duplikációhoz, duplikált útvonalakhoz, duplikált tesztekhez és az üzemeltetési kézikönyvekben megjelenő „Melyik verziót használjuk egyáltalán?“-problémához vezet.

Egy alternatíva a verziózás a Media Type (Content Negotiation) alapján. A kliens például elküldi: Accept: application/vnd.company.order+json;v=2. A szerver determinisztikusan kiolvassa a verziót és a Contract/DTO-viselkedést ennek megfelelően igazítja. Ez működik proxy- és cache-láncokban, ha a headereket tisztán továbbítják. Az adminok számára is jól ellenőrizhető: egy request reprodukálható Curl/Postman segítségével anélkül, hogy az URL-ek különböznének.

A RemObjects SDK nem „REST-purista”, hanem egy pragmatikus service-framework. Pont ezért éri meg a média-típus megközelítés: megtarthatók a stabil végpontok, miközben a szerződések továbbfejleszthetők. Fontos, hogy a verziót mindig kiértékeljék, egy helyen központosítva döntsék el, és az eredményt átadják a szolgáltatás kontextusának.

Mikor bukik meg az Accept-Header megoldás?

Gyakorlatban három tipikus töréspont van, amelyet előre érdemes kezelni:

  • Proxy-Policies: Egyes reverse proxy-k/WAF-szabályok normalizálják vagy szűrik az Accept-headereket. Ilyenkor az API csendben visszaesik az alapértelmezettre. Megoldás: a proxy-szabályok kifejezetten ellenőrzése, szükség esetén a X-Api-Version használata tartalékként.
  • Client-Libraries: Néhány HTTP-kliens saját Accept-headert állít be és felülírja az értékeket. Megoldás: a contract-verzió opcionálisan támogatható lekérdezési paraméterként (csak fallbackként), vagy az Accept-header szerveroldalon toleránsan legyen parse-olva.
  • Gyorsítótárazás: Ha a válaszok gyorsítótárazása szerepet játszik, a cache-nek az Accept szerint kell variálnia (Vary: Accept), különben az 1-es verziót adja a 2-es verziójú klienseknek. Megoldás: a Vary tudatos beállítása, vagy a gyorsítótárazás letiltása az API szinten.
  • Forrásrészlet: Request-Context, Correlation-ID, Verzió és Error-Mapping

    A kód szándékosan úgy van kialakítva, hogy meglévő RemObjects-szerverprojektekbe integrálható: egy kis kontextusréteg, egy az API-verziót (Accept-ből) feldolgozó parszoló, egy Correlation-ID mechanizmus és egy központi Exception-Mapping. Fogalmak:

    • Correlation-ID: Egyedi azonosító minden kéréshez, amely megjelenik a válaszban és hivatkozható a naplókban.
    • Exception-Mapping: Belső Delphi-kivételek lefordítása stabil, kliens által feldolgozható hibaobjektumokká (beleértve az HTTP-státuszt).
    • Contract-Version: A JSON-szerződés verziója, amely a viselkedést és a mezőket szabályozza.
    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.

    Cél: stabil Request-kontextus a „irgendwo im Threadlocal” helyett

    A snippet szándékosan különválaszt: TApiContext az a minimális állapot, amit tovább szeretne adni. A RemObjects SDK esetében sok minden a szerver-/csatorna-konteksten keresztül működik. Heterogén projektekben (pl. további worker-szálak, DB-sor, háttérfeladatok) a kontextus expliciten történő továbbadása gyakran robosztusabb, mint az implicit threadlocalok, mert így a párhuzamosság és a kontextusváltások láthatóbbak lesznek.

    Előfeltételek: Az Accept-header alapú megoldás feltételezi, hogy az Ön reverse proxy-ja (nginx, IIS ARR, Traefik) a headert változtatás nélkül továbbítja. Egyes környezetekben a „szokatlan” Accept-headereket szűrik vagy összefésülik.

    Buktatók: A verzionálás Accept-en keresztül annyira jó, amennyire a tesztjei. Ha a kliensek olyan könyvtárakat használnak, amelyek felülírják az Accept-et, az API hirtelen az alapértelmezet használhatja. Legacy-kliensek esetén érdemes Default-fallbacket biztosítani, de ennek láthatónak kell lennie a monitoringban (pl. napló-figyelmeztetés „Version defaulted”).

    Variánsok: Ha a verzionálást inkább X-Api-Version headeren keresztül végzi: a parser azonos, csak a forrás egy másik header. Gateway-k szempontjából ez néha könnyebben kontrollálható.

    Integráció a RemObjects SDK-ban: Correlation-ID és Exception-Mapping a szolgáltatás belépési pontján

    A valódi hatás akkor jön létre, ha a mechanikát következetesen alkalmazza a szerver szélén: egyszer a request belépésénél headerből olvasni, egyszer a kivétel kilépésénél stabil response-ba lefordítani. A hostingtól függően (pl. RO-HTTP-Server, IIS-hosting, saját üzemeltetésű Windows-/Windows- und Linux-Services) eltérnek a konkrét hook-pontok; az elv ugyanaz: kontextus felépítése, üzleti logika meghívása, kivételek központi mappolása.

    RemObjects-projektekben gyakran közvetlenül, szolgáltatás-metódusonként dolgoznak. Ez kezdetben jól skáláz, de üzemeltetéskor problémákhoz vezet: minden metódus másként valósítja meg a naplózást és a hibakezelést. Egy tiszta határ egy Service-alap osztály vagy egy Dispatcher, amely standardizál.

    Gyakorlati folyamat (szándékosan rövid és implementáció-közeli)

    1. Correlation-ID beolvasása a kérés fejlécéből X-Correlation-ID; ha hiányzik, szerveroldalon generálni (pl. GUID).
    2. Szerződés-verzió olvasása az Accept-ből (vagy az X-Api-Version-ből).
    3. Kérés indítása naplózása: metódus, útvonal, Correlation-ID, távoli IP; időmérés indítása.
    4. Üzleti logika végrehajtása; adatbázis-hozzáféréseket lehetőleg tranzakciókba kapszulálni.
    5. Kivétel elkapása: HTTP-státusz meghatározása, JSON-hibobjektum létrehozása, válasz fejléc X-Correlation-ID beállítása.
    6. Kérés vége naplózása: státusz, időtartam, szükség szerint hibakód.

    Szálkezelés a szerveren: miért lesz értéktelen a Correlation-ID kontextus-fegyelem nélkül

    Egy gyakori Delphi-szél-eset: a szolgáltatásmetódus aszinkron munkát indít (pl. riportgenerálás, import, push egy DMS-be). Ekkor az eredeti kérés szála már nem az, amely később a naplóbejegyzéseket írja. Ha a Correlation-ID csak „az elején” ismert, a követhetőség szétesik.

    Pragmatikus szabály: minden, ami nem marad szigorúan a request-szálon, megkapja a kontekstust expliciten átadva. Még ha ez több paraméterlistának tűnik is, megtérül. Alternatívaként lehet egy jól definiált kontextus-objektummal dolgozni, amelyet tudatosan átadnak a worker-eknek (a globális változók vagy rejtett singletonok helyett).

    Jellemző töréspontok RemObjects-/Delphi-szervereken:

    • DB-Connections pro Thread: 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“.
    • Tranzakciós határok: Ha egy kérésen belül több egymáshoz tartozó lépés van, a tranzakciónak ugyanabban a logikai egységben kell maradnia. Az aszinkron munka nem folytatódhat „véletlenül” ugyanabban a tranzakcióban.
    • Mégse: Ha a kliens megszakítja a kapcsolatot (proxy timeout, böngésző bezárva), a szerver gyakran tovább fut. Gondolja át tudatosan, hogy a háttérmunka ilyenkor még értelmes-e.

    Adatlekérés és hibakódok: 409 ist nicht „auch ein 500”

    Integrációs projektekben a tiszta hiba-mapping többet jelent, mint kozmetika. Ettől függ, hogy egy partner (ERP-Connector, ETL-job, Ügyfélportál) helyesen tud-e reagálni. Néhány gyakorlati iránymutatás, amelyek Delphi/RemObjects-környezetekben beváltak:

    • 400 Bad Request: Validációs hibák, hiányzó/érvénytelen paraméterek, JSON nem parseolható. Fontos: a válasznak stabilnak kell maradnia, még akkor is, ha a törzs hibás.
    • 401/403: Különválasztani a hitelesítést és a jogosultságot. 401 azt jelenti: „nincs/érvénytelen identitás”, 403: „identitás rendben, de tiltott”.
    • 404: Az erőforrás nem létezik. Biztonsági megfontolás: ne mindig fedje fel, hogy valami létezik-e.
    • 409 Conflict: Üzleti/koncepcionális konfliktus (pl. verzióütközés, „az állapot nem engedi meg ezt a műveletet”, egyedi kulcs megsértése, ha az üzletileg releváns).
    • 422 Unprocessable Content: Ha szintaktikailag minden rendben van, de az üzleti validáció megbukik (nem minden csapat használja a 422-t, de gyakran egyértelműbb, mint a 400).
    • 500: Minden, amit nem tudnak egyértelműen osztályozni. Ide tartozik például az „adatbázis leállt”, „timeout” vagy a „kezeletlen kivétel”.

    Delphi-specifikus tipp: Sok adatbázis-hiba generikus kivételként emelkedik fel. Érdemes az adat-hozzáférési rétegben célzottan felmérni ismert helyzeteket és azokat EApiError-be átalakítani. Fontos: ne vegyen át SQL-töredékeket vagy belső táblanév-/oszlopneveket a kliensüzenetbe. Ezek a részletek a logban legyenek, ne a válaszban.

    Debugging-tipp: reprodukálható hibák durch „Contract Snapshot”

    Szokatlan, de üzemeltetésben rendkívül hasznos: hibák esetén (vagy célzottan bizonyos Correlation-IDs-hez kapcsolódóan) mentse el a kérés fejléceiből és törzséből egy „snapshot”-ot egy debug-spool fájlba. Ez nem folyamatos naplózás (adatvédelmi/terjedelmi okokból), hanem egy kontrollált eszköz a nehezen reprodukálható, éles környezet közeli esetek visszaállításához.

    Fontos: egy snapshot soha nem tárolhat szűretlenül Auth-Header-eket, tokeneket vagy személyes adatokat. A gyakorlatban ez redactionot (maszkolást) és csak Feature-Flag vagy Whitelist alapú aktiválást jelent (pl. csak bizonyos Correlation-ID-khez, rövid időablakokban).

    Gyakorlati megvalósítás: Maszkolás a kihagyás helyett

    Valódi integrációkban gyakran éppen a „kritikus” mezők azok, amikre szükség van a hibakereséshez (pl. azonosítók). Ahelyett, hogy ezeket általánosan kihagynánk, jobb őket maszkolni: a tokeneket részlegesen helyettesíteni, az e-mailből csak a domaint megtartani, az IBAN-ból csak az utolsó számjegyeket. Így a hiba reprodukálható marad anélkül, hogy felesleges adatok kerülnének a fájlrendszerbe. Emellett a snapshot legyen egyértelműen jelölve mint debug-artefakt és rendelkezzen meghatározott tárolási idővel.

    Biztonság és üzemeltetés: fejléc-átadás, proxy-láncok és időkorlátok

    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: Ne támaszkodjon vakon a X-Forwarded-For-ra. Csak megbízható proxy-kból vegye át, különben használja a közvetlen socket-IP-t. Az üzemeltetési kézikönyvben legyen rögzítve, mely hopok „trusted” státuszúak.
    • Timeouts: Ha a proxy 30 másodpercet állít be, az Ön backendje viszont 2 percet igényel, ghost-requesteket generál. Állítsa be az időkorlátokat következetesen a lánc mentén, és döntsön: szinkron kérés vagy job-pattern (202 Accepted + Status-Endpunkt).
    • Correlation-ID: Tegye be a Correlation-ID-t a response-fejlécekbe, hogy az adminok logokból és kliensoldalról össze tudják kapcsolni. Ha egy gateway saját request-ID-ket használ: naplózza és térképezze fel mindkét azonosítót.
    • Fehlertexte: Termelési környezetben ne adjon ki belső részleteket. Debug-részleteket csak kontrollált környezetben (Stage/Feature-Flag) jelenítsen meg, és ha kétséges, csak a logba írja őket.

    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: Egyértelmű szolgáltatáshatárok: a szolgáltatási metódusok explicit-ek, az API-szerződések verziózhatók.
    • Transporte und Serialisierung: Transzportok és szerializáció: használhat JSON-t, de igény szerint más üzenetformátumokat is, anélkül, hogy összekeverné az üzleti logikát.
    • Betrieb: Üzemeltetés: a hoszting-opciók és a meglévő Windows- und Linux-Services irányába történő integráció tervezhető, beleértve a tiszta rolloutokat.

    A bemutatott megközelítés kiegészíti ezeket azokkal az elemekkel, amelyek a gyakorlatban gyakran hiányoznak: egységes hibajellegű objektumok, determinisztikus verziózás és korrelálható naplózás. Különösen egyedi vállalati szoftvereknél, hosszú élettartam mellett ez időt takarít meg frissítéseknél és külső rendszerek integrációjánál.

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

    A hozzáadott érték akkor keletkezik, ha az Ön REST-felülete nem csupán „működik”, hanem tartósan üzemeltethető: stabil JSON-szerződések, verziózás URL-wildwouthoz nélkül, nyomon követhető hibák és találgatás nélküli hibakeresés. Pontosan ebben erős a megközelítés a RemObjects SDK-ban: Context, Correlation-ID és központi Exception-Mapping.

    Einsatzgrenzen: Ha csak egyetlen, rövid életű végpontja van, integrációs partnerek nélkül, a Media-Type-Versionierung gyorsan Overengineering-nek tűnhet. A Snapshot-Logging szintén csak akkor értelmes, ha a Redaction és az aktiválás fegyelmezetten meg van valósítva. És: ha a proxy-stack eltávolítja vagy „optimalizálja” a headereket, előbb az infrastruktúrát kell rendbe tenni, különben a rossz réteget fogja hibakeresni.

    Ha egy meglévő Delphi-szerverlandscapet modernizálni kell, vagy egy folyamathoz közeli szoftvermegoldást tisztán be kell integrálni ERP/DMS/CRM rendszerekbe, ezek a mechanizmusok gyakran a különbséget jelentik a „teszten fut” és az „üzemben fut” állapot között.

    A szakmai környezetben szintén fontos szerepet játszanak a Delphi REST-API és REST-szerver és a Remobjects Sdk Delphi, ha az integrációknak, az adatáramlásoknak és a továbbfejlesztésnek pontosan össze kell hangolódniuk.

    Projekt vagy modernizációs kezdeményezés megbeszélése a Net-Base csapatával.

    Következő lépés

    Ha a téma valós projektté válik, az architektúrát, a meglévő rendszert és az üzemeltetést már korán együtt kell értékelni.

    Nemcsak egyedi kérdésekben támogatunk, hanem akkor is, amikor forráskódrészletekből, örökölt rendszerekkel kapcsolatos témákból vagy portálötletekből robusztus vállalati projektet kell kialakítani.

    • A jelenlegi állapotot, a célállapotot és a műszaki kockázatokat együttesen értékeljük.
    • REST, az adathozzáférés, a portálok és a Rollout nem kerülnek utólagos teendőkként elhalasztásra.
    • Már korán láthatja, melyik út gazdaságilag és üzemeltetési szempontból életképes.

    Bejegyzés megosztása

    Ezt a bejegyzést közvetlenül megosztani

    LinkedIn, X, XING, Facebook, WhatsApp és e-mail azonnal elérhetők. Instagramhoz linket és rövid szöveget közvetlenül előkészítünk.

    E-mail

    Az Instagram egy új lapon nyílik meg. A link és a rövid szöveg előzetesen a vágólapra másolódik.