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-val” a szélsőséges esetekben

Egy REST API RemObjects SDK-val ritkán esik szét a „Hello World”-szolgáltatásnál; a döntő pontok ott vannak, ahol az üzemeltetés, a legacy és az integráció találkozik: verziókezelés állásidő nélkül, konzisztens hibakezelés minden végponton, reprodukálható hibakeresés proxy-láncok esetén és a képeség egy kérés egyértelmű korrelálására probléma esetén.

A RemObjects SDK sok infraelemet ad ehhez: szolgáltatások, üzenetformátumok, serializáció, hosting (pl. mint Windows- és Linux-szolgáltatások vagy IIS/Reverse Proxy mögött) és definiált pontok a hibák központi kezelésére. Ami azonban 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 ismerünk fel egy kérést újra, ha az Load Balancer-en, TLS-termináción és több backend rétegen áthaladt?

A következő megközelítés (beleértve egy Delphi-kódrészletet) egy robusztus irányvonalat mutat a RemObjects SDK-hoz: JSON-szerződések verziózása, a Correlation-ID (Request-ID a nyomon követéshez) kikényszerítése, Exceptions átképzése HTTP státuszokká és JSON-hibaobjektumokká, miközben a hibakeresés és az üzemeltetés érdekeit nem szembeállítjuk. Emellett megnézzük azokat a szélestestű eseteket, amelyek valódi környezetekben rendszeresen előfordulnak: szerveroldali threading, adatbázis-hozzáférések BDE-kiváltás natív illesztéssel, proxy-fejlécek, timeoutek és „szennyezett” kliens-payloadok.

Architektúra-döntés: verziózás média-típus (Media Type) alapján URL helyett

Sok API a verziót az olyan útvonalakon kezeli, mint a /v1/. Ez pragmatikus, de hosszabb távú integrációkban (pl. ERP/DMS/CRM kapcsolódások) gyakran URL-duplikációhoz, duplikált route-okhoz, duplikált tesztekhez és az „Melyik verziót használjuk valójában?“ kérdéshez vezet az üzemeltetési kézikönyvekben.

Egy alternatíva a verziózás a media type (content negotiation) segítségével. A kliens például küldheti: Accept: application/vnd.company.order+json;v=2. A szerver determinisztikusan kiolvassa a verziót és a contract/DTO viselkedést ennek megfelelően állítja be. Ez működik proxy- és cache-láncokban, ha a header-eket tisztán továbbítják. Az adminok számára is jól ellenőrizhető: egy request reprodukálható Curl/Postman-nel úgy, hogy az URL-ek nem változnak.

A RemObjects SDK nem „REST-purista“, hanem egy pragmatikus szolgáltatáskeret. Pont ezért érdemes a média-típus alapú megoldást alkalmazni: megtarthatók a stabil végpontok, miközben a szerződéseket tovább lehet fejleszteni. Fontos, hogy a verziót mindig kiértékeljék, egy központi ponton döntsék el és az eredményt átvigyék a szolgáltatás kontextusába.

Mikor bukik meg az Accept-header megközelítés?

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

  • Proxy-szabályok: Egyes reverse proxy-k/WAF szabályok normalizálják vagy megszűrik az Accept-header-t. Ilyenkor az API észrevétlenül visszaesik az alapértelmezett verzióra. Megoldás: ellenőrizze kifejezetten a proxy-szabályokat, szükség esetén térjen át X-Api-Version használatára.
  • Client-könyvtárak: Néhány HTTP-kliens saját Accept-header-t állít be és felülírja az értékeket. Megoldás: a contract-verziót támogassa opcionális query-paraméterként is (csak fallbackként), vagy a szerveroldalon legyen toleráns az Accept-header feldolgozása.
  • Caching: Ha válaszok gyorsítótárazása (Response-Caching) aktív, a gyorsítótárnak az Accept fejléc alapján kell változnia (Vary: Accept), különben 1-es verziójú választ szolgál ki 2-es verziójú klienseknek. Megoldás: Vary tudatos beállítása, vagy a gyorsítótárazás letiltása az API-szinten.

Forráskódrészlet: Request-Context, Correlation-ID, verzió és hibatérképezés

A kód szándékosan úgy van felépítve, hogy beilleszthető legyen meglévő RemObjects-szerverprojektekbe: egy kisebb Context-réteg, egy parser az API-verzióhoz (a Accept-ből), egy Correlation-ID-mechanizmus és egy központi kivétel-leképezés. Fogalmak:

  • Correlation-ID: Egyedi azonosító kérésenként, amely megjelenik a válaszban és hivatkozható a naplókban.
  • Exception-Mapping: Belső Delphi-kivétel(ek) átalakítása stabil, kliens által feldolgozható hibajobjektumokká (beleértve az HTTP státuszt).
  • Contract-Version: A JSON-szerződés verziója, amely meghatározza a viselkedést és a mezőket.
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 kódrészlet szándékosan elválaszt: TApiContext az a minimális állapot, amit Ön továbbadni szeretne. A RemObjects SDK-ban sok minden a szerver-/csatorna-konteksten keresztül fut. Heterogén projektekben (pl. további worker-threadek, DB-queue, háttérfeladatok) a kontextus explicitt átadása gyakran robusztusabb, mint az implicit Threadlocal használata, mert így a párhuzamosság és a kontextusváltások jobban láthatóvá válnak.

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

Veszélyforrások: Verziózás Accept-en keresztül csak olyan jó, mint a tesztjei. Ha a kliens könyvtárakat használ, amelyek felülírják az Accept-et, az API hirtelen a defaultra eshet vissza. Legacy kliens esetén logikus egy default-fallback, de ennek láthatónak kell lennie a monitoringban (pl. Log-figyelmeztetés: „Version defaulted”).

Variánsok: Ha a verziózást inkább a X-Api-Version headerrel végzi: a parser azonos, csak a forrása más header. Gateway-k szempontjából ez néha egyszerűbben kontrollálható.

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

A tényleges hatás akkor jön létre, ha a mechanikát következetesen az Ön szervere szélén alkalmazza: egyszer a request belépésénél a headerekből kiolvasni, egyszer az exception kimeneténél stabil response-á fordítani. A hosting-tól függően (pl. RO-HTTP-Server, IIS-hosting, saját üzemeltetésű Windows-/Windows- és Linux-Services) a konkrét hook-pontok eltérnek; az elv ugyanaz: Context felépítése, business-logika meghívása, Exception-ök központi mappolása.

RemObjects-projektekben gyakori, hogy közvetlenül szolgáltatás-módszinten dolgoznak. Ez kezdetben jól skáláz, de üzem közben problémákhoz vezet: minden metódus másként építi fel a logolást és a hibakezelést. Egy tiszta határvonal egy service-alap vagy egy standardizált dispatcher, ami egységesen kezeli ezt.

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

  1. Correlation-ID olvasása a Request-headerből X-Correlation-ID; ha hiányzik, szerver-oldalon generálni (pl. GUID).
  2. Contract-version olvasása az Accept-ből (vagy a X-Api-Version-ból).
  3. Request kezdete logolása: metódus, útvonal, Correlation-ID, távoli IP, időmérés indítása.
  4. Business-logika végrehajtása; adatbázis-hozzáféréseket lehetőleg tranzakciósan kapszulázni.
  5. Exception elkapása: HTTP státusz meghatározása, JSON-hibobjektum előállítása, Response-header X-Correlation-ID beállítása.
  6. Request vége logolása: státusz, időtartam, esetleg hiba-kód.

Threading a szerveren: miért válik értelmetlenné a Correlation-ID kontextus-disciplína nélkül

Egy gyakori Delphi-végponti eset: a szolgáltatás-módszer aszinkron munkát indít (pl. riportgenerálás, import, push egy DMS-be). Ilyenkor az eredeti request-thread már nem az, amely később log-sorokat ír. Ha a Correlation-ID csak „az elején” ismert, a nyomonkövethetőség felbomlik.

Pragmatikus szabály: Minden, ami nem marad szigorúan a request-threaden, kapja meg a Context expliciten átadva. Bár ez hosszabb paraméterlistákat eredményezhet, megtérül. Alternatívaként egy jól definiált kontextus-objektummal dolgozhatunk, amelyet tudatosan átadnak a workereknek (a globális változók vagy rejtett singletonok helyett).

Tipikus töréspontok RemObjects-/Delphi-szervereken:

  • Szálankénti DB-kapcsolatok: BDE-Ablosung mit nativer Anbindung-kapcsolatok nem oszthatók meg automatikusan thread-biztos módon. Egy Connection-pool vagy szálanként egy kapcsolat gyakran célszerűbb, mint „egy globális kapcsolat”.
  • 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. Aszinkron munka nem folytatódhat „véletlenül” ugyanabban a tranzakcióban.
  • Cancellation: Ha a kliens megszakítja a kapcsolatot (Proxy timeout, Browser closed), a szerver gyakran továbbfut. Mérlegelje tudatosan, van-e értelme ilyenkor a háttérmunkának.

Adat-hozzáférés és hibakódok: 409 ist nicht „auch ein 500”

Az integrációs projektekben a tiszta hibamapping több mint kozmetika. Eldönti, hogy egy partner (ERP-Connector, ETL-Job, Ügyfélportál) helyesen tud-e reagálni. Néhány gyakorlatias iránymutatás, amelyek beváltak Delphi/RemObjects környezetekben:

  • 400 Bad Request: Érvényesítés, hiányzó/érvénytelen paraméterek, JSON nem feldolgozható. Fontos: a válasznak stabilnak kell maradnia akkor is, ha a body sérült.
  • 401/403: Hitelesítést és jogosultságot külön kell választani. A 401 jelentése „nincs/érvénytelen identitás”, a 403 „identitás rendben, de tiltott”.
  • 404: Az erőforrás nem létezik. Biztonsági megfontolások: nem mindig szabad felfedni, hogy valami létezik-e.
  • 409 Conflict: Üzleti konfliktus (pl. verzióütközés, „a státusz nem engedélyezi ezt a műveletet”, egyedi kulcs megsértése, ha az üzletileg releváns).
  • 422 Unprocessable Content: Ha szintaktikailag minden rendben van, de üzleti érvényesítés sikertelen (nem minden csapat használja a 422-t, de gyakran egyértelműbb, mint a 400).
  • 500: Minden, amit nem tud megfelelően osztályozni. Ide tartozik a „DB down”, „Timeout”, „Unhandled Exception” is.

Delphi-specifikus trükk: Sok DB-hiba generikus kivételként érkezik fel. Megéri az adat-hozzáférési rétegben célzottan felismerni ismert helyzeteket és azokat EApiError-be átvinni. Fontos: Ne adjon át SQL-töredékeket vagy belső táblák-/oszlopok neveit a kliens üzenetében. Ezek a részletek a logba valók, nem a válaszba.

Debugging-trükk: reprodukálható hibák „Contract Snapshot” segítségével

Szokatlan, de üzemeltetésben rendkívül hasznos: Hibák esetén (vagy célzottan bizonyos Correlation-ID-knél) mentsen el egy „Snapshot”-ot a Request-headerekből + Request-body-ból egy debug spool fájlba. Ez nem állandó logolás (adatvédelmi/terjedelmi okok), hanem egy kontrollált eszköz a nehezen reprodukálható esetek éles közeli utánozásához.

Fontos: Egy snapshot soha nem tárolhat szűretlenül Auth-headereket, tokeneket vagy személyes adatokat. A gyakorlatban ez redactionot (maszkolást) és csak Feature-Flag vagy whitelist általi aktiválást jelent (pl. csak bizonyos Correlation-ID-kre, rövid időablakokra).

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

Valódi integrációkban gyakran éppen a „kritikus” mezők azok, amelyekre a hibakereséshez szükség van (pl. azonosítók). Az általános kihagyás helyett jobb a maszkolás: a tokenek részleges helyettesítése, az e-mail címből csak a domain megtartása, az IBAN-ból csak az utolsó számjegyek megtartása. Így a hiba reprodukálható marad anélkül, hogy felesleges adatok terjednének a fájlrendszerben. Emellett a snapshotot egyértelműen debug-artefaktumként kell megjelölni, és meghatározott megőrzési időt kell neki adni.

Biztonság és üzemeltetés: fejléc-továbbítás, proxy-láncok és időkorlátok

Egy REST API ritkán ér véget közvetlenül a kliensnél. Tipikusak a reverse proxy, TLS-terminálás, WAF vagy API-gateway láncok. Ebből gyakorlati szempontok adódnak:

  • Távoli IP: Ne támaszkodjon vakon a X-Forwarded-For-ra. Csak megbízható proxyktó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 tekinthetők megbízhatónak.
  • Időkorlátok: Ha a proxy 30 másodperces határidőt tart, az Ön backendje viszont 2 percet igényel, fantomkéréseket (ghost-requests) generál. Állítsa be következetesen az időkorlátokat a lánc mentén, és döntse el: szinkron kérést kezel, vagy job-patternt alkalmaz (202 Accepted + státusz-végpont).
  • Correlation-ID: Tegye be a Correlation-ID-t a válaszfejlécekbe, hogy az adminok a naplók és a kliensoldal adatai alapján összekapcsolhassák a műveleteket. Ha egy gateway saját Request-ID-ket használ, mindkét azonosítót logolja és ábrázolja.
  • Hibaüzenetek: Éles üzemben ne adjon ki belső részleteket. Hibakeresési részletek csak kontrollált környezetben legyenek elérhetők (stage/feature-flag), és szükség esetén csak a naplóban legyenek megtalálhatók.

Elhelyezés: Miért lehet a RemObjects SDK ebben előnyös

A Delphi-ökoszisztémában a REST-szervereket gyakran könnyebb keretrendszerekkel (pl. minimalista HTTP-routerek) építik. A RemObjects SDK akkor játssza ki erősségét, ha már meglévő vagy szükséges egy többrétegű architektúra:

  • Egyértelmű szolgáltatási határok: A szolgáltatásmetódusok explicit módon definiáltak, a kontraktusok verziózhatók.
  • Transzportok és szerializáció: Beszélhet JSON-nal, de szükség szerint más üzenetformátumokat is használhat, anélkül hogy a domainlogikát összekeverné.
  • Üzemeltetés: A hoszting-opciók és a meglévő Windows- és Linux-szolgáltatások integrációja tervezhető, beleértve a szabályozott bevezetéseket.

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

Következtetés: Megéri az erőfeszítés — és hol válik problémássá a megközelítés?

Az érték akkor keletkezik, ha az Ön REST-felülete nem csak „működik”, hanem hosszú távon üzemeltethető: stabil JSON-szerződések, verziózás URL-szaporodás nélkül, nyomon követhető hibák és találgatás nélküli hibakeresés. Pont itt erős a megközelítés a Context, Correlation-ID és a központi Exception-Mapping alkalmazásával a RemObjects SDK-ban.

Alkalmazási korlátok: Ha csak egyetlen, rövid életű végpontja van integrációs partnerek nélkül, a Media-Type-verziózás könnyen túlkomplikálttá válhat. A snapshot-naplózás szintén csak akkor indokolt, ha a Redaction és az aktiválás fegyelmezetten van megvalósítva. És: ha a proxy-stack a fejléceket „optimalizálja” vagy eltávolítja, először az infrastruktúrát kell rendbe hoznia, különben a rossz réteget fogja hibakeresni.

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

A szakmai környezetben továbbá fontos szerepet játszanak a Delphi REST-API és REST-szerver és a Remobjects Sdk Delphi, amikor az integrációknak, az adatfolyamoknak és a további fejlesztésnek zavartalanul együtt kell működniük.

Projektet vagy modernizációs tervet egyeztessen a Net-Base-szel.

Következő lépés

Ha egy témából valós projekt lesz, az architektúrát, a meglévő rendszert és az üzemeltetést korai fázisban együtt kell vizsgálni.

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ést, a portálokat és a bevezetést nem halasztjuk későbbi fázisokra.
  • Ön korán látja, melyik út gazdaságilag és üzemeltetési szempontból tartható.

Bejegyzés megosztása

Ezt a bejegyzést közvetlenül megosztani

LinkedIn, X, XING, Facebook, WhatsApp és E-Mail azonnal elérhetők. Instagramra a linket és a rövid szöveget közvetlenül előkészítjük.

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.