Net-Base Časopis

09.06.2026

REST API sa RemObjects SDK: JSON krajnje tačke jasno verzionisati i debugovati (Delphi izvorni isječak)

Kako pomoću RemObjects SDK u Delphi izgraditi REST API koji u radu ostaje stabilan: stabilni JSON ugovori, verzioniranje bez neurednog množenja URL-ova, Correlation-ID kroz sve slojeve, centralno mapiranje grešaka, snapshot-logiranje 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 mit RemObjects SDK“ u praksi često odlučuje na rubovima

Jedna REST API mit RemObjects SDK rijetko stoji ili pada na „Hello World“-servisu, već na mjestima gdje se operacije, naslijeđeni sustavi i integracija sudaraju: verzioniranje bez zastoja, konzistentno ponašanje pri pogreškama preko svih endpointa, reproduktivno debagiranje u lancima proxyja i sposobnost jednoznačnog koreliranja zahtjeva u slučaju problema.

RemObjects SDK za to donosi puno infrastrukture: servise, formate poruka, serijalizaciju, hosting (npr. kao Windows- und Linux-Services ili iza IIS/Reverse Proxy) i definirane točke za centralno upravljanje greškama. Ono što u izgrađenim poslovnim softverskim okruženjima često nedostaje jest dosljedno proveden ugovor: Koja JSON polja su stabilna? Kako signaliziramo pogreške? Kako ponovno prepoznamo zahtjev nakon što je prošao kroz load balancer, TLS-terminaciju i više backend slojeva?

Sljedeći pristup (uključujući Delphi-snipete) pokazuje robusnu liniju za RemObjects SDK: verzionirati JSON ugovore, forsirati Correlation-ID (Request-ID za praćenje), prevoditi Exceptions u HTTP-status i JSON-objekte pogrešaka i pri tome ne postavljati debugiranje i operacije u suprotstavljene uloge. Dodatno razmatramo rubne slučajeve koji se u realnim okruženjima redovno pojavljuju: threading na serveru, pristupi bazi podataka s BDE-ablacijom uz nativno povezivanje, proxy-headeri, timeouti i „nečisti“ client-payloadi.

Arhitektonska odluka: verzioniranje preko medija tipa umjesto URL-a

Mnoge API-je se verzioniraju putem putanja poput /v1/. To je pragmatično, ali u dugotrajnim integracijama (npr. ERP/DMS/CRM povezivanja) često vodi do dupliranja URL-ova, duplih ruta, duplih testova i pitanja „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 pročita verziju i prilagodi ponašanje ugovora/DTO-a. To funkcionira u lancima proxyja i keširanja, pod uvjetom da se headeri pravilno prenesu. Za administratore je dodatno provjerljivo: zahtjev se može reproducirati putem Curl/Postman bez izmjene URL-ova.

RemObjects SDK nije „REST-puristički“, već pragmatičan servisni okvir. Upravo zbog toga varijanta s medijskim tipom ima smisla: možete zadržati stabilne endpoint-e i istovremeno razvijati ugovore. Važno je da verziju uvijek procjenjujete, centralno odlučite i rezultat unesete u kontekst vašeg servisa.

Kada varijanta s Accept-headerom zakaže?

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

  • Proxy-Policies: Neki reverse proxyji / WAF pravila normaliziraju ili filtriraju Accept-header. U tom slučaju vaša API tiho pada na zadanu verziju. Rješenje: eksplicitno provjeriti proxy pravila, po potrebi koristiti X-Api-Version kao zamjenu.
  • Client-Libraries: Neki HTTP-klijenti postavljaju vlastite Accept-headere i prepisuju vrijednosti. Rješenje: podržati verziju ugovora i kao opcionalni query-parametar (samo kao fallback), ili server-side tolerantno parsirati Accept-header.
  • Caching: Ako se koristi Response-Caching, cache mora varirati prema Accept (Vary: Accept), inače će isporučivati verziju 1 klijentima verzije 2. Rješenje: svjesno postaviti Vary ili onemogućiti keširanje na nivou API-ja.

Izvorni isječak: Kontekst zahtjeva, Correlation-ID, verzija i mapiranje grešaka

Kod je namjerno sročen tako da se može integrirati u postojeće RemObjects-Serverprojekte: mali kontekstni sloj, parser za verziju API-ja (iz Accept), mehanizam Correlation-ID i centralno Exception-Mapping. Pojmovi:

  • Correlation-ID: Jedinstveni identifikator po zahtjevu koji se vraća u odgovoru i na koji se referencira u logovima.
  • Exception-Mapping: Prevođenje internih Delphi-Exceptions u stabilne objekte grešaka koje klijent može obraditi (uključujući HTTP-status).
  • Contract-Version: Verzija JSON-kontrakta koja određuje ponašanje i polja.
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“

Snippeta svjesno razdvaja: TApiContext je minimalno stanje koje želite prosljeđivati. U RemObjects SDK mnogo se radi preko server-/channel-konteksta. U heterogenim projektima (npr. dodatni worker-threadovi, DB-queue, pozadinski poslovi) eksplicitno prosljeđivanje često je robusnije od implicitnih Threadlocal-a, jer time konkurentnost i promjene konteksta činite vidljivijima.

Randbedingungen: Varijanta preko Accept-headera pretpostavlja da vaš reverse proxy (nginx, IIS ARR, Traefik) header proslijedi nepromijenjen. U nekim okruženjima „neobični“ Accept-headeri se filtriraju ili agregiraju.

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

Varianten: Ako verzioniranje radije radite preko X-Api-Version: parser je identičan, samo je izvor drugi header. S aspekta gateway-a to je ponekad lakše kontrolirati.

Integracija u RemObjects SDK: Correlation-ID i Exception-Mapping na ulazu servisa

Stvarna korist nastaje kad mehaniku konzistentno primijenite na rubu servera: jedanput pri ulazu requesta pročitate iz headera, drugiput pri izlazu iz izuzetka prevedete u stabilan response. Ovisno o hostingu (npr. RO-HTTP-Server, IIS-hosting, samostalno upravljani Windows-/Windows- i Linux-servisi) konkretne hook-punkte se razlikuju; princip ostaje isti: izgraditi kontekst, pozvati poslovnu logiku, centralno mapirati exception-e.

U RemObjects-projektima često se radi direktno po pojedinoj metodi servisa. To u početku skalira, ali se u radu raspada: svaka metoda gradi logging i obradu grešaka drugačije. Čist odvajanje je service-baza ili dispatcher koji standardizira.

Praktični postupak (namjerno kratak i implementacijski orijentisan)

  1. Pročitajte Correlation-ID iz headera zahtjeva X-Correlation-ID; ako nedostaje, generirajte ga server-side (npr. GUID).
  2. Pročitajte verziju contracta iz Accept (ili iz X-Api-Version).
  3. Zalogirajte početak requesta: metoda, path, Correlation-ID, remote IP, započnite mjerenje trajanja.
  4. Izvršite poslovnu logiku; DB-pristupe kapsulirajte transakcijski kad god je moguće.
  5. Uhvati exception: odredite HTTP-status, kreirajte JSON-objekt greške, postavite response-header X-Correlation-ID.
  6. Zalogirajte kraj requesta: status, trajanje, eventualno error-kod.

Threading na serveru: Zašto Correlation-ID bez disciplne konteksta gubi vrijednost

Čest Delphi-rubni slučaj: metoda servisa pokreće asinhroni rad (npr. generiranje reporta, import, push u DMS). Tada izvorni request-thread više nije onaj koji kasnije piše log-linije. Ako je Correlation-ID poznat samo „na početku“, sljedivost se raspada.

Pragmatično pravilo: Sve što ne ostaje striktno u request-threadu treba dobiti kontekst eksplicitno proslijeđen. Iako to daje duže liste parametara, isplati se. Alternativno se radi s jasno definisanim objektom konteksta koji se svjesno predaje workeru (umjesto globalnih varijabli ili skrivenih singleton-a).

Tipične tačke prekida u RemObjects-/Delphi-serverima:

  • DB-veze po niti: BDE-Ablosung mit nativer Anbindung-Verbindungen nisu automatski sigurno deljive među nitima. Connection-pool ili po niti jedna veza često je smislenije nego „globalna veza“.
  • Transakcijske granice: Ako unutar jednog zahtjeva imate više koraka koji pripadaju istoj logičkoj jedinici, transakcija mora ostati unutar iste logičke jedinice. Asinhroni rad ne smije „slučajno“ nastaviti u istoj transakciji.
  • Otkazivanje: Kada klijent prekine (Proxy timeout, zatvoren preglednik), server često nastavlja raditi. Promislite svjesno ima li pozadinski rad tada još smisla.

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

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

  • 400 Bad Request: Validacija, nedostajući/neispravni parametri, JSON se ne može parsirati. Važno: Odgovor treba ostati stabilan čak i ako je tijelo oštećeno.
  • 401/403: Razdvojite autentikaciju i autorizaciju. 401 znači „nema/nevažeći identitet“, 403 „identitet u redu, ali zabranjeno“.
  • 404: Resurs ne postoji. Pažnja kod sigurnosti: Ne otkrivajte uvijek postojanje resursa.
  • 409 Conflict: Poslovni konflikt (npr. konflikt verzija, „status ne dozvoljava ovu akciju“, kršenje jedinstvenog ključa, ako je to relevantno za domen).
  • 422 Unprocessable Content: Ako je sintaksa u redu, ali poslovna validacija ne uspijeva (ne koristi svaki tim 422, ali je često jasnije od 400).
  • 500: Sve što ne možete jasno klasificirati. Tu spadaju i „DB down“, „Timeout“, „Unhandled Exception“.

Delphi-specifičan trik: Mnoge DB-greške dolaze kao generičke iznimke. Isplati se, na sloju pristupa podacima, ciljano provjeriti poznate situacije i mapirati ih u EApiError. Važno pri tome: Ne uključivati SQL-fragmente ili unutrašnja imena tabela/kolona u poruku klijentu. Ti detalji pripadaju logu, ne odgovoru.

Debugging-trik: reproduktivne greške kroz „Contract Snapshot“

Neobično, ali u radu izuzetno korisno: Spremite pri greškama (ili ciljano za određene Correlation-IDs) jedan „snapshot“ od zaglavlja zahtjeva + tijela zahtjeva u Debug-Spool-datoteku. To nije stalno logiranje (zaštita podataka/volumen), već kontrolirani alat za reproduciranje teško reproducibilnih slučajeva blizu produkcije.

Važno: Snapshot nikada ne smije nefiltrirano trajno pohraniti Auth-header-e, tokene ili osobne podatke. U praksi to znači: redakcija (maskiranje) i aktivacija samo putem Feature-Flag ili bijele liste (npr. samo za određene Correlation-IDs, kratki vremenski prozori).

Ispravna implementacija 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-mail ostaviti samo domenu, IBAN ostaviti samo posljednje znamenke. Tako je slučaj reproducibilan, bez nepotrebnog širenja podataka po datotečnom sistemu. Dodatno, snapshot bi trebao biti jasno označen kao debug-artefakt i imati definiran period čuvanja.

Sigurnost i operativni rad: prosljeđivanje zaglavlja, proxy lanci i vremenska ograničenja

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. Preuzimajte ga samo od pouzdanih proxyja i u suprotnom koristite direktnu socket-IP adresu. U operativnim priručnicima treba biti navedeno koji su hopovi „trusted“.
  • Timeouts: Ako proxy ima 30 sekundi, a vašem backendu treba 2 minute, generirat ćete ghost-requests. Postavite timeoute konzistentno duž cijelog lanca i odlučite: sinhroni zahtjev ili job-pattern (202 Accepted + Status-Endpunkt).
  • Correlation-ID: Postavite Correlation-ID u response-header-e kako bi administratori mogli objediniti zapise iz logova i s klijentske strane. Ako gateway koristi vlastite Request-IDs: logirajte obe ID-e i mapirajte ih.
  • Fehlertexte: U produkciji bez internih detalja. Debug-detalje dozvolite samo kontrolisano (Stage/Feature-Flag) i, u sumnji, samo 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: Service-Methoden sind explizit, Kontrakte sind verzionierbar.
  • Transporte und Serialisierung: Sie können JSON sprechen, aber auch andere Message-Formate (je nach Setup), ohne die Fachlogik zu verquirlen.
  • Betrieb: Hosting-Optionen und Integration in bestehende Windows- und Linux-Services sind planbar, inklusive sauberer Rollouts.

Der gezeigte Ansatz ergänzt das um die Teile, die im Alltag oft fehlen: einheitliche Fehlerobjekte, deterministische Versionierung und korrelierbares Logging. Gerade bei individueller Unternehmenssoftware mit langen Lebenszyklen sparen Sie damit Zeit bei Updates und bei der Integration externer Systeme.

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

Der Mehrwert entsteht, wenn Ihre REST-Schnittstelle nicht nur „funktioniert“, sondern dauerhaft betreibbar ist: stabile JSON-Verträge, Versionierung ohne URL-Wildwuchs, nachvollziehbare Fehler und Debugging ohne Ratespiel. Genau dort ist der Ansatz mit Context, Correlation-ID und zentralem Exception-Mapping in RemObjects SDK stark.

Einsatzgrenzen: Wenn Sie nur einen einzelnen, kurzlebigen Endpunkt ohne Integrationspartner haben, wirkt Media-Type-Versionierung schnell wie Overengineering. Auch Snapshot-Logging ist nur sinnvoll, wenn Sie Redaction und Aktivierung diszipliniert implementieren. Und: Wenn Ihr Proxy-Stack Header „optimiert“ oder entfernt, müssen Sie zuerst die Infrastruktur geradeziehen, sonst debuggen Sie die falsche Schicht.

Wenn Sie eine bestehende Delphi-Serverlandschaft modernisieren oder eine prozessnahe Softwarelösung sauber in ERP/DMS/CRM integrieren müssen, sind genau diese Mechanismen aber häufig der Unterschied zwischen „läuft im Test“ und „läuft im Betrieb“.

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

Razgovarajte o projektu ili planu modernizacije sa Net-Base.

Sljedeći korak

Kada se tema pretvori u stvarni projekat, arhitektura, postojeći sistem i operacije trebaju se rano sagledati zajedno.

Pružamo podršku ne samo pri pojedinačnim pitanjima, već i kada iz fragmenata izvornog koda, naslijeđenih sistema ili ideja za portal treba nastati robustan poslovni projekat.

  • Postojeće stanje, ciljno stanje i tehnički rizici procjenjuju se zajedno.
  • REST, pristup podacima, portali i Rollout se ne odgađaju kao naknadne posljedice.
  • Vi rano vidite koji je put ekonomski i operativno održiv.

Podijeli objavu

Ovu objavu direktno proslijediti

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 novom tabu. Link i kratak tekst se prethodno kopiraju u međuspremnik.