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

Jedan REST API s RemObjects SDK rijetko posluje ili pada na „Hello World“-servisu, nego na mjestima gdje operacije, legacy i integracija dolaze u konflikt: verzioniranje bez zastoja, konzistentno ponašanje pri greškama preko svih endpointa, reproducibilno debuggiranje kroz lance proxyja i sposobnost jednoznačne korelacije requestova u slučaju problema.

RemObjects SDK donosi mnogo infrastrukture: servise, formate poruka, serializaciju, hosting (npr. kao Windows- i Linux-servisi ili iza IIS/Reverse Proxy) i definirane točke za centralno upravljanje greškama. Ono što u razvijenim poslovnim softverskim okruženjima često nedostaje jest dosljedno proveden ugovor: koja JSON polja su stabilna? Kako signaliziramo greške? Kako ponovno identificirati request koji je prošao kroz load balancer, TLS-terminaciju i više backend-slojeva?

Sljedeći pristup (uključujući Delphi-snippete) pokazuje robusnu liniju za RemObjects SDK: verzioniranje JSON ugovora, Correlation-ID (Request-ID za praćenje) nametnuti, mapiranje Exceptions u HTTP-status i JSON objekt(e) grešaka i pritom ne suprotstavljati debugiranje i operativu. Dodatno sagledavamo rubne slučajeve koji se u stvarnim okruženjima redovito pojavljuju: upravljanje nitima na serveru, pristupi bazi podataka s BDE-zamjena s native vezom, proxy zaglavlja, timeouti i „prljave“ klijentske payloadove.

Arhitektonska odluka: verzioniranje preko medijskog tipa umjesto URL-a

Mnoge API-je verzioniraju preko putanja kao što je /v1/. To je pragmatično, ali u dugotrajnim integracijama (npr. ERP/DMS/CRM konekcije) često vodi dupliciranju URL-ova, dvostrukim rutama, dvostrukim testovima i pitanju „koju verziju zapravo koristimo?“ u operativnim priručnicima.

Alternativa je verzioniranje preko Media Type (Content Negotiation). Klijent pošalje npr. Accept: application/vnd.company.order+json;v=2. Server deterministički iščita verziju i prilagodi ponašanje contracta/DTO-a. To funkcionira u lancima proxyja i cache-a ako se headeri ispravno prosljeđuju. Za administratore je dodatno lako provjerljivo: request se može reproducirati preko curl-a/Postmana bez promjene URL-ova.

RemObjects SDK nije „REST-purističan“, već pragmatičan service-framework. Upravo zato varijanta preko medijskog tipa ima smisla: možete zadržati stabilne endpoint-e i istovremeno razvijati ugovore. Važno je da verziju uvijek evaluirate, da centralno odlučite i da rezultat unesete u kontekst vašeg servisa.

Kada varijanta s Accept-headerima zakaže?

U praksi postoje tri uobičajene kritične točke koje treba unaprijed adresirati:

  • Proxy-Policies: Neki reverse proxyji/WAF pravila normaliziraju ili filtriraju Accept-zaglavlja. Tada vaša API tiho padne na default. Rješenje: eksplicitno provjeriti proxy-pravila, po potrebi preći na X-Api-Version.
  • Client-Libraries: Neki HTTP-klijenti postavljaju vlastita Accept-zaglavlja i prepisuju vrijednosti. Rješenje: podržati verziju contracta i kao opcionalni query-parametar (samo kao fallback), ili parsati Accept-zaglavlje server-side tolerantno.
  • Keširanje: Ako se koristi keširanje odgovora, keš mora varirati prema Accept (Vary: Accept), inače će isporučiti verziju 1 klijentima verzije 2. Rješenje: eksplicitno postaviti Vary ili onemogućiti keširanje na razini API-ja.

Izvorni isječak: Request-Context, Correlation-ID, verzija i mapiranje pogrešaka

Kod je namjerno složen tako da se može integrirati u postojeće RemObjects-server projekte: mala sloj za kontekst, parser za verziju API-ja (iz Accept), mehanizam Correlation-ID i centralno mapiranje iznimki. Pojmovi:

  • Correlation-ID: Jedinstveni ID po zahtjevu koji se vraća u odgovoru i na koji se referencira u logovima.
  • Exception-Mapping: Prevođenje internih Delphi-Exceptions u stabilne, klijentima obradive objekte pogrešaka (uključujući HTTP-status).
  • Contract-Version: Verzija JSON ugovora 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 ThreadLocalu”

Ovaj isječak svjesno odvaja: TApiContext je minimalno stanje koje želite proslijediti. U RemObjects SDK mnogo toga se odvija preko Server-/Channel-Kontexts. U heterogenim projektima (npr. dodatni Worker-Threads, DB-Queue, zadaci u pozadini) eksplicitno prosljeđivanje je često robusnije od implicitnih Threadlocals, jer time činite konkurentnost i promjene konteksta vidljivijima.

Preduvjeti: Varijanta s Accept-headerom pretpostavlja da vaš reverse proxy (nginx, IIS ARR, Traefik) prosljeđuje header nepromijenjen. U nekim okruženjima se „neobični“ Accept-headeri filtriraju ili spajaju.

Zamke: Verzije preko Accept su samo toliko pouzdane koliko i vaši testovi. Ako klijenti koriste biblioteke koje prepisuju Accept, API može iznenada pasti na zadanu vrijednost. Za legacy klijente smislen je zadani fallback, ali on mora biti vidljiv u monitoringu (npr. log upozorenje „Version defaulted“).

Varijante: Ako radije želite verzioniranje preko X-Api-Version: parser je identičan, samo je izvor drugi header. Iz perspektive gatewaya to je ponekad lakše kontrolirati.

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

Stvarna korist nastaje kada mehaniku konzekventno primijenite na rubu vašeg servera: jednom pri ulasku zahtjeva pročitati iz headera, jednom pri izlazu iz iznimke prevesti u stabilan response. Ovisno o hostingu (npr. RO-HTTP-Server, IIS-hosting, vlastiti Windows-/Windows- und Linux-Services) razlikuju se konkretne hook-punkte; princip ostaje isti: sastaviti kontekst, pozvati poslovnu logiku, centralno mapirati iznimke.

U RemObjects projektima se često radi izravno po svakoj metodi servisa. To na početku dobro skalira, ali u radu može postati problematično: svaka metoda uređuje logging i obradu grešaka na svoj način. Čist rez je service-baza ili dispatcher koji standardizira te korake.

Praktični postupak (namjerno kratak i implementacijski orijentiran)

  1. Pročitati Correlation-ID iz Request-Headera X-Correlation-ID; ako nedostaje, generirati ga na serverskoj strani (npr. GUID).
  2. Pročitati verziju kontrakta iz Accept (ili iz X-Api-Version).
  3. Zabilježiti početak zahtjeva: metoda, putanja, Correlation-ID, udaljena IP, započeti mjerenje trajanja.
  4. Izvršiti poslovnu logiku; pristupe bazi po mogućnosti kapsulirati transakcijski.
  5. Uhvatiti iznimku: odrediti HTTP status, stvoriti JSON objekt greške, postaviti Response-Header X-Correlation-ID.
  6. Zabilježiti kraj zahtjeva: status, trajanje, eventualno kod greške.

Threading na serveru: Zašto Correlation-ID bez discipline konteksta postaje bezvrijedna

Čest rubni slučaj u Delphi-okruženjima: metoda servisa pokreće asinkroni rad (npr. generiranje izvještaja, import, push u DMS). Tada izvorni request-thread više nije onaj koji kasnije zapisuje logove. Ako je Correlation-ID poznata samo „na početku“, pratljivost se raspada.

Pragmatično pravilo: Sve što ne ostane striktno u request-threadu treba dobiti kontekst eksplicitno. Čak i ako to izgleda kao duže liste parametara, isplati se. Alternativno se može raditi s jasno definiranim objektom konteksta koji se svjesno prosljeđuje workerima (umjesto globalnih varijabli ili skrivenih singletona).

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

  • DB-veze po niti: BDE-Ablosung mit nativer Anbindung-veze nisu automatski sigurno dijeljive između niti. Connection-pool ili po niti vlastita veza često je prikladnija od „globalne veze“.
  • Granice transakcije: Ako unutar jednog zahtjeva imate više koraka koji pripadaju zajedno, transakcija mora ostati u istoj logičkoj jedinici. Asinkroni rad ne smije se „slučajno“ nastaviti u istoj transakciji.
  • Prekid (Cancellation): Ako klijent prekine (proxy timeout, zatvoren preglednik), server često nastavlja raditi. Promislite svjesno ima li pozadinski rad tada još smisla.

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

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

  • 400 Bad Request: Validacija, nedostajući/nerazumni parametri, JSON se ne može parsirati. Važno: Odgovor treba ostati stabilan čak i ako je body korumpiran.
  • 401/403: Razdvojite autentifikaciju i autorizaciju. 401 znači „nema/neispravni identitet“, 403 znači „identitet u redu, ali zabranjeno“.
  • 404: Resurs ne postoji. Pažnja u pogledu sigurnosti: ne treba uvijek otkrivati postoji li nešto.
  • 409 Conflict: Strukturni/funkcionalni 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 sintaksa u redu, ali poslovna validacija ne prolazi (ne koriste sva tima 422, ali često je jasnije od 400).
  • 500: Sve što ne možete jasno klasificirati. To uključuje i „DB nedostupna“, „timeout“, „Unhandled Exception“.

Delphi-specifični trik: Mnoge greške baze podataka dolaze kao generičke iznimke. Vrijedi ciljano na sloju pristupa podacima provjeriti poznate situacije i prevesti ih u EApiError. Važno pri tome: Ne preuzimajte fragmente SQL-a ili interne nazive tablica/kolona u poruku klijentu. Ti se detalji trebaju zapisati u log, a ne u response.

Trik za debugiranje: reproducibilne greške pomoću „Contract Snapshot“

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

Važno: Snapshot nikad ne smije nefiltrirano persistirati auth-zaglavlja, tokene ili osobne podatke. U praksi to znači: redaction (maskiranje) i aktivacija samo preko feature-flaga ili whiteliste (npr. samo za određene Correlation-ID-e, kratka vremenska prozora).

Čista provedba u praksi: maskiranje umjesto izostavljanja

U stvarnim integracijama upravo su „kritična“ polja često ona koja su potrebna za debug (npr. identifikatori). Umjesto općeg izostavljanja, bolje je maskiranje: djelomično zamijeniti tokene, e‑poštu zadržati samo domenu, IBAN samo posljednje znamenke. Tako je slučaj reproducibilan bez nepotrebnog širenja podataka po datotečnom sustavu. Dodatno, snapshot treba jasno označiti kao artefakt za debugiranje i imati definirano vrijeme čuvanja.

Sigurnost i rad: prosljeđivanje zaglavlja, lanci proxyja i timeouti

Jedna REST API rijetko završava izravno kod klijenta. Tipični su lanci reverse proxyja, TLS-terminacije, WAF ili API-gateway. Iz toga proizlaze praktične točke:

  • Remote IP: Nemojte se slijepo pouzdavati u X-Forwarded-For. Prihvaćajte ga samo od pouzdanih proxyja, u suprotnom koristite izravnu socket-IP. U priručnicima za rad treba navesti koji hopovi „trusted“ su.
  • Timeouts: Ako proxy ima 30 sekundi, a vaše backend treba 2 minute, stvarate ghost-zahtjeve. Postavite timeoute dosljedno duž lanca i odlučite: sinkroni zahtjev ili job-pattern (202 Accepted + statusni endpoint).
  • Correlation-ID: Postavite Correlation-ID u response-zaglavlja, kako bi administratori mogli upariti zapise iz logova i klijentske strane. Ako gateway koristi vlastite Request-ID-e: logirajte i mapirajte obje ID-e.
  • Fehlertexte: U produkciji bez internih detalja. Debug-detalje samo kontrolirano (Stage/Feature-Flag) i, u nedoumici, samo u logu.

Einordnung: Warum RemObjects SDK hier im Vorteil sein kann

U Delphi-ekosustavima se REST-Server često grade s lakšim frameworkima (npr. minimalistički HTTP-routeri). RemObjects SDK pokazuje svoju snagu kada već imate ili trebate višeslojnu arhitekturu:

  • Klare Service-Grenzen: Jasne granice servisa: metode servisa su eksplicitne, kontrakti su verzionabilni.
  • Transporte und Serialisierung: Transporti i serializacija: Možete koristiti JSON, ali i druge formate poruka (ovisno o postavkama), bez miješanja poslovne logike.
  • Betrieb: Operativnost: Opcije hostinga i integracija u postojeće Windows- und Linux-Services su planabilne, uključujući uredne rolloute.

Pokazani pristup nadopunjuje to dijelovima koji često nedostaju u svakodnevici: jedinstveni objekti pogrešaka, determinističko verzioniranje i korelirajuće logiranje. Posebno kod individualnog poslovnog 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 „funkcionira“, već je i dugoročno održivo: 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 Exception-Mappingom u RemObjects SDK snažan.

Einsatzgrenzen: Ako imate samo jedan pojedinačni, kratkotrajan endpoint bez integracijskih partnera, Media-Type-verzioniranje brzo djeluje kao overengineering. Snapshot-logiranje također ima smisla samo ako disciplinirano implementirate Redaction i Aktivierung. I: ako vaš proxy-stack zaglavlja „optimiert“ ili uklanja, prvo morate srediti infrastrukturu, inače debugirate pogrešni sloj.

Ako modernizirate postojeće Delphi-serversko okruženje ili trebate čisto integrirati procesno blisko softversko rješenje u ERP/DMS/CRM, upravo su ti mehanizmi često razlika između „läuft im Test“ i „läuft im Betrieb“.

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

Razgovarajte o projektu ili planu modernizacije s Net-Base.

Sljedeći korak

Kad se tema pretvori u stvarni projekt, arhitektura, postojeći sustav i operativni rad trebaju se rano sagledati zajedno.

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 kasne posljedice.
  • Vidite rano koji je put ekonomski i operativno održiv.

Podijeli objavu

Izravno proslijedite ovu objavu

LinkedIn, X, XING, Facebook, WhatsApp i e-mail su odmah dostupni. Za Instagram pripremamo link i kratak tekst.

E-pošta

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