Net-Base Časopis

09.06.2026

REST API s RemObjects SDK-om: uredno verzioniranje i debugiranje JSON krajnjih točaka (Delphi isječak izvornog koda)

Kako pomoću RemObjects SDK u Delphi izgraditi REST API koji u radu ne kolabira: stabilni JSON-ugovori, verzioniranje bez divljanja URL-ova, Correlation-ID kroz sve slojeve, centralno mapiranje pogrešaka, Snapshot-Logging za zahtjevne debug-slučajeve te praktični savjeti...

09.06.2026

Od teme magazina do projektne prakse

Povezane stranice usluga i tehnologije za članak

Zašto „REST API s RemObjects SDK“ u praksi često odlučuje na rubovima

Jedna REST API s RemObjects SDK rijetko stoji ili pada na „Hello World“ servisu, nego na mjestima gdje se operacije, Legacy i integracija sukobljavaju: verzioniranje bez zastoja, dosljedno ponašanje pri greškama preko svih krajnjih točaka, reproducibilno debugiranje kod lanaca proxyja i sposobnost jednoznačne korelacije zahtjeva u slučaju problema.

RemObjects SDK donosi za to puno infrastrukture: servise, message-formate, serijalizaciju, hosting (npr. kao Windows- i Linux-servisi ili iza IIS/Reverse Proxy) i definirane točke za centralno rukovanje greškama. Ono što u zrelim poslovnim softverskim okruženjima često nedostaje jest dosljedno proveden kontrakt: koja JSON-polja su stabilna? Kako signaliziramo greške? Kako ponovno prepoznati zahtjev kad je prošao kroz load balancer, TLS-Termination i više slojeva backenda?

Sljedeći pristup (uključujući Delphi-Snipsel) prikazuje robusnu liniju za RemObjects SDK: versionirati JSON-kontrakte, prisiliti Correlation-ID (Request-ID za praćenje), prevesti Exceptions u HTTP-status i JSON-objekte greške i pritom ne suprotstavljati debugiranje i operativni rad. Dodatno razmatramo rubne slučajeve koji se u stvarnim okruženjima redovito pojavljuju: upravljanje nitima na serveru, pristupi bazi podataka s BDE-zamjenom uz nativnu vezu, proxy-zaglavlja, timeouti i „prljave“ klijentske payload-e.

Arhitektonska odluka: verzioniranje putem medijskog tipa umjesto URL-a

Mnoge API-je verzioniraju putem putanja poput /v1/. To je pragmatično, ali u dugotrajnim integracijama (npr. ERP/DMS/CRM-povezivanjima) često dovodi do dupliciranja URL-ova, duplih ruta, duplih testova i pitanja „Koju verziju zapravo koristimo?“ u operativnim priručnicima.

Jedna alternativa je verzioniranje preko Media Type (Content Negotiation). Klijent pošalje npr. Accept: application/vnd.company.order+json;v=2. Server deterministički iščitava verziju i prilagođava ponašanje kontrakta/DTO-a. To funkcionira u lancima proxyja i cache-a ako se zaglavlja pravilno prosljeđuju. Za administratore je dodatno dobro provjerljivo: zahtjev se može reproducirati putem Curl/Postman-a bez promjene URL-ova.

RemObjects SDK nije „REST-purističan“, već pragmatični service-framework. Upravo zato se isplati varijanta medijskog tipa: možete zadržati stabilne krajnje točke i ipak razvijati kontrakte. Važno je da verziju uvijek iščitavate, na jednom mjestu centralno odlučite i rezultat preuzmete u kontekst vašeg servisa.

Kada varijanta Accept-zaglavlja zakaže?

U praksi postoje tri tipične točke loma koje treba unaprijed adresirati:

  • Proxy-Politike: Neki Reverse Proxies/WAF-pravila normaliziraju ili filtriraju Accept-zaglavlje. U tom slučaju vaša API tiho pada na zadano. Rješenje: eksplicitno provjeriti proxy-pravila, po potrebi se osloniti na X-Api-Version.
  • Klijentske biblioteke: Neki HTTP-klijenti postavljaju vlastita Accept-zaglavlja i prepisuju vrijednosti. Rješenje: podržati verziju kontrakta i kao opcionalni query-parametar (samo kao fallback), ili na strani servera tolerantno parsirati Accept-zaglavlje.
  • Caching: Ako je prisutan Response-Caching, keš mora varirati prema Accept (Vary: Accept), inače isporučuje verziju 1 klijentima verzije 2. Rješenje: svjesno postaviti Vary ili onemogućiti keširanje na razini API-ja.

Isječak izvornog koda: Request-Context, Correlation-ID, verzija i mapiranje pogrešaka

Kod je namjerno koncipiran tako da se može integrirati u postojeće RemObjects-Serverprojekte: mali sloj konteksta, parser za verziju API-ja (iz Accept), mehanizam Correlation-ID i centralno mapiranje iznimki. Pojmovi:

  • Correlation-ID: Jedinstveni ID po zahtjevu koji se pojavljuje u odgovoru i na koji se referencira u zapisima.
  • Exception-Mapping: Prijevod internih Delphi-Exceptions u stabilne, za klijenta obradive objekte pogrešaka (uključujući HTTP-status).
  • Contract-Version: Verzija JSON-kontrakta koja upravlja ponašanjem i poljima.
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.

Svrha: stabilan kontekst zahtjeva umjesto „negdje u Threadlocal“

Ovaj isječak namjerno razdvaja: TApiContext je minimalno stanje koje želite prosljeđivati. U RemObjects SDK mnogo toga ide preko server-/channel-konteksta. U heterogenim projektima (npr. dodatne radne dretve, DB-queue, pozadinski jobovi) eksplicitno prosljeđivanje često je robusnije od implicitnih Threadlocal varijabli, jer time konkurentnost i promjene konteksta činite vidljivijima.

Uvjeti: Varijanta s Accept zaglavljem pretpostavlja da vaš reverse proxy (nginx, IIS ARR, Traefik) prosljeđuje zaglavlje nepromijenjeno. U nekim okruženjima se „neobična“ Accept zaglavlja filtriraju ili agregiraju.

Zamke: Verzioniranje preko Accept-a vrijedi onoliko koliko su vaši testovi. Ako klijenti koriste biblioteke koje prepisuju Accept, API može iznenada pasti na default. Za legacy-klijente je default-fallback smislen, ali mora biti vidljiv u monitoringu (npr. log-opomena „Version defaulted“).

Varijante: Ako želite verzioniranje raditi preko X-Api-Version: parser je identičan, jedino je izvor drugi header. S aspekta gatewaya to je ponekad lakše kontrolirati.

Integracija u RemObjects SDK: Correlation-ID i mapiranje iznimki pri ulazu u servis

Pravi efekt nastaje kada mehaniku dosljedno primijenite na rubu vašeg servera: jednom pri ulasku zahtjeva čitati iz zaglavlja, jednom pri izlazu iz iznimke prevesti u stabilan odgovor. Ovisno o hostingu (npr. RO-HTTP-Server, IIS-Hosting, samostalno upravljani Windows-/Windows- und Linux-Services) konkretne točke za hook razlikuju se; princip ostaje isti: izgraditi Context, pozvati poslovnu logiku, centralno mapirati Exceptions.

U RemObjects-projektima često se radi direktno po metodi servisa. To na početku dobro skalira, ali u produkciji posustaje: svaka metoda implementira Logging i obradu grešaka različito. Čist rez je jedna Service-Basis ili Dispatcher koji standardizira.

Praktični postupak (namjerno kratak i blizak implementaciji)

  1. Pročitati Correlation-ID iz zaglavlja zahtjeva X-Correlation-ID; ako nedostaje, generirati ga na serveru (npr. GUID).
  2. Pročitati Contract-Version iz Accept (ili iz X-Api-Version).
  3. Zabilježiti početak zahtjeva: metodu, putanju, Correlation-ID, udaljenu IP, pokrenuti mjerenje trajanja.
  4. Izvršiti poslovnu logiku; DB-pristupe po mogućnosti kapsulirati transakcijski.
  5. Uhvatiti Exception: odrediti HTTP-status, generirati JSON-objekt greške, postaviti Response-header X-Correlation-ID.
  6. Zabilježiti kraj zahtjeva: status, trajanje, po potrebi kod greške.

Dretvovanje na serveru: zašto Correlation-ID bez discipliniranog konteksta postaje bezvrijedna

Čest rubni slučaj Delphi: metoda servisa pokreće asinkroni rad (npr. generiranje izvještaja, import, push u DMS). Tada početna dretva zahtjeva više nije ona koja kasnije piše logove. Ako je Correlation-ID poznata samo „na početku“, praćenje se raspada.

Pragmatično pravilo: sve što se ne zadržava strogo u dretvi zahtjeva dobije kontekst eksplicitno proslijeđen. Iako to izgleda kao dulje liste parametara, isplati se. Alternativno se može raditi s jasno definiranim objektom konteksta koji se svjesno prosljeđuje worker-ima (umjesto globalnih varijabli ili skrivenih singletona).

Tipične točke prijeloma u RemObjects-/Delphi-serverima:

  • DB-veze po niti: 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 „globalna veza“.
  • Granice transakcije: Ako unutar jednog Requesta imate više koraka koji pripadaju zajedno, transakcija mora ostati u istoj logičkoj jedinici. Asinkroni rad ne smije „slučajno“ nastaviti u istoj transakciji.
  • Otkaživanje: Ako klijent prekine (Proxy timeout, zatvoren preglednik), server često nastavi raditi. Promislite svjesno ima li smisla tada nastavljati pozadinski rad.

Pristup podacima i kodovi pogrešaka: 409 ist nije „također 500“

U integracijskim projektima čisto mapiranje grešaka je više od kozmetike. Ono odlučuje može li suprotna strana (ERP-Connector, ETL-Job, portal za korisnike) ispravno reagirati. Nekoliko praktičnih smjernica koje su se pokazale u Delphi/RemObjects okruženjima:

  • 400 Bad Request: Validacija, nedostajući/nevažeći parametri, JSON se ne može parsirati. Važno: Odgovor treba ostati stabilan čak i ako je tijelo oštećeno.
  • 401/403: Autentikacija i autorizacija razdvojeno. 401 znači „nema/nevažeći identitet“, 403 „identitet u redu, ali zabranjeno“.
  • 404: Resurs ne postoji. Pažnja zbog sigurnosti: ne otkrivati uvijek postoji li nešto.
  • 409 Conflict: Poslovni konflikt (npr. konflikt verzija, „status ne dopušta ovu akciju“, kršenje jedinstvenog ključa ako je relevantno za poslovnu logiku).
  • 422 Unprocessable Content: Ako je sintaktički sve u redu, ali poslovna validacija ne prođe (ne koristi svaki tim 422, ali često je jasnije od 400).
  • 500: Sve što ne možete jasno klasificirati. To uključuje i „DB down“, „Timeout“, „Unhandled Exception“.

Delphi-specifičan trik: Mnoge DB-pogreške dolaze kao generičke iznimke. Isplati se u sloju za pristup podacima ciljano provjeravati poznate situacije i prevesti ih u EApiError. Važno: Ne uključivati SQL-fragmente ili interne nazive tablica/kolona u poruku prema klijentu. Ti detalji pripadaju u log, ne u odgovor.

Trik za debugiranje: reproducibilne pogreške kroz „Contract Snapshot“

Neobično, ali u radu izuzetno korisno: pri greškama (ili ciljano za određene Correlation-ID-e) spremite „snapshot“ od zaglavlja zahtjeva + tijela zahtjeva u debug-spool datoteku. To nije trajno logiranje (zaštita podataka/volumen), nego kontrolirani alat za rekonstrukciju teško reproducibilnih slučajeva iz produkcije.

Važno: Snapshot nikada ne smije nefiltrirano pohranjivati auth-header, tokene ili osobne podatke. U praksi to znači: Redaction (maskiranje) i aktivacija samo preko Feature-Flag ili Whitelist (npr. samo za određene Correlation-ID-e, kratka vremenska razdoblja).

Ispravna implementacija u praksi: Maskiranje umjesto izostavljanja

U stvarnim integracijama upravo su „kritična“ polja često ona koja su potrebna za debugiranje (npr. identifikatori). Umjesto općeg izostavljanja, bolje je maskirati: djelomično zamijeniti tokene, zadržati samo domenu e-pošte, IBAN samo zadnje znamenke. Tako je slučaj reproducibilan, bez razbacivanja nepotrebnih podataka po datotečnom sustavu. Dodatno, snapshot bi trebao biti jasno označen kao debug-artifakt i imati definirano vrijeme čuvanja.

Sigurnost i operacija: prosljeđivanje zaglavlja, proxy-lanci i timeouti

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 oslanjajte se slijepo na X-Forwarded-For. Prihvaćajte ga samo od pouzdanih proxyja, u suprotnom koristite izravnu socket-IP adresu. U priručnicima za rad treba navesti koji su hopovi „trusted“.
  • Timeouts: Ako proxy ima 30 sekundi, a vaše backend treba 2 minute, stvarate ghost-requests. Postavite timeout-e konzistentno duž lanca i odlučite: sinkroni request ili job-pattern (202 Accepted + status-endpoint).
  • Correlation-ID: Postavite Correlation-ID u zaglavlja odgovora, kako bi administratori mogli povezati zapise iz logova i klijentske strane. Ako gateway koristi vlastite Request-IDs: evidentirajte i mapirajte obje ID-e.
  • Fehlertexte: U produkciji ne otkrivajte interne detalje. Debug-detalje izlažite samo kontrolirano (stage/feature-flag) i po potrebi isključivo u logu.

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: Metode servisa su eksplicitne, ugovori se mogu verzionirati.
  • Transporte und Serialisierung: Možete koristiti JSON, ali i druge formate poruka (ovisno o postavkama), bez miješanja poslovne logike.
  • Betrieb: Opcije hostinga i integracija u postojeće Windows- und Linux-Services mogu se planirati, uključujući uredne rolloute.

Prikazani pristup nadopunjuje to dijelovima koji u svakodnevnoj praksi često nedostaju: jedinstveni objekti pogrešaka, determinističko verzioniranje i korrelirajuće logiranje. Posebno kod individualnog korporativnog softvera s dugim životnim ciklusima, time štedite vrijeme pri nadogradnjama i pri integraciji vanjskih sustava.

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

Dodana vrijednost nastaje kada vaše REST-sučelje ne samo da „funkcionira“, već je dugoročno upravljivo: stabilni JSON-ugovori, verzioniranje bez URL-divljanja, razumljive pogreške i debugiranje bez nagađanja. Upravo tu je pristup s Context, Correlation-ID i centralnim mapiranjem iznimaka u RemObjects SDK snažan.

Einsatzgrenzen: Ako imate samo jedan kratkotrajan endpoint bez integracijskih partnera, Media-Type-Verzionierung brzo djeluje kao overengineering. Snapshot-Logging također ima smisla samo ako disciplinirano implementirate redaction i aktivaciju. I: ako vaš proxy-stack zaglavlja „optimizira“ ili uklanja, prvo morate dovesti infrastrukturu u red, inače ćete debugirati pogrešni sloj.

Ako modernizirate postojeće Delphi-okruženje servera ili trebate uredno integrirati procesno blisko softversko rješenje u ERP/DMS/CRM, upravo ti mehanizmi često čine razliku između „radi u testu“ i „radi u produkciji“.

U stručnom okruženju također važnu ulogu imaju Delphi REST-API i REST-Server i Remobjects Sdk Delphi kada se integracije, tokovi podataka i daljnji razvoj moraju uredno uskladiti.

Projekt ili plan modernizacije raspravite s Net-Base.

sljedeći korak

Ako se tema pretvori u stvarni projekt, arhitekturu, postojeće sustave i operacije trebalo bi rano zajednički razmotriti.

Podržavamo vas ne samo u pojedinačnim pitanjima, već i kada iz isječaka izvornog koda, naslijeđenih sustava ili ideja za portale treba nastati pouzdan poslovni projekt.

  • Postojeće stanje, ciljna slika i tehnički rizici procjenjuju se zajedno.
  • REST, pristup podacima, portali i rollout neće biti odgođeni kao naknadne posljedice.
  • Rano prepoznajete koji je put ekonomski i operativno održiv.

Podijeli objavu

Izravno proslijedite ovu objavu

LinkedIn, X, XING, Facebook, WhatsApp i e-pošta su odmah dostupni. Za Instagram odmah pripremamo poveznicu i kratak tekst.

E-pošta

Instagram se otvara u novoj kartici. Link i kratki tekst se prethodno kopiraju u međuspremnik.