Net-Base Магазин

09.06.2026

REST API са RemObjects SDK: прецизно верзионисање и дебаговање JSON крајњих тачака (Delphi исечак изворног кода)

Како да са RemObjects SDK у Delphi изградите REST API који у раду неће отказивати: стабилни JSON-уговори, верзионисање без бесконтролног множeња URL-ова, Correlation-ID кроз све слојеве, централизовано мапирање грешака, Snapshot-логовање за тешке случајеве дебаговања као и практични савети...

09.06.2026

Од теме часописа до пројектне праксе

Одговарајуће странице услуга и техничке странице за чланак

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

Jedan REST API mit RemObjects SDK retko puca ili opstaje zbog „Hello World“ servisa, već na mestima gde se sudaraju operacije, legacy i integracija: verzionisanje bez zastoja, konzistentno ponašanje pri greškama na svim endpointima, ponovljivo otklanjanje grešaka kroz proxy lance i sposobnost da se zahtevi u slučaju problema jednoznačno korališu.

RemObjects SDK pruža za to mnogo infrastrukture: servise, formate poruka, serijalizaciju, hosting (npr. kao Windows- и Linux-servisi ili iza IIS/Reverse Proxy) i definisane tačke za centralizovano rukovanje greškama. Ono što u razvijenim poslovnim softverskim okruženjima često nedostaje je dosledan ugovor: koja JSON polja su stabilna? Kako signaliziramo greške? Kako ponovo identifikujemo zahtev kada je prošao kroz load balancer, TLS-terminaciju i više backend slojeva?

Sledeći pristup (uključujući Delphi-primeri) pokazuje robusnu liniju za RemObjects SDK: versionisanje JSON-ugovora, Correlation-ID (ID zahteva za praćenje) forsirati, prevođenje Exceptions u HTTP status i JSON objekte grešaka i pritom ne posmatrati debugovanje i operacije kao antagoniste. Pored toga razmatramo ivične slučajeve koji se u realnim okruženjima redovno javljaju: upravljanje nitima na serveru, pristupi bazi podataka pri BDE-zameni sa nativnom vezom, proxy-hedere, timeout-e i „neuredne“ klijentske payload-ove.

Arhitektonska odluka: verzionisanje preko media-type umesto URL-a

Mnoge API-je se verzionisu putem putanja kao /v1/. To je pragmatično, ali u dugotrajnim integracijama (npr. ERP/DMS/CRM povezivanja) često vodi duplikaciji URL-ova, duplim rutama, duplim testovima i situacijama „koju verziju zapravo koristimo?“ u uputstvima za rad.

Alternativa je verzionisanje preko media type-a (content negotiation). Klijent pošalje npr. Accept: application/vnd.company.order+json;v=2. Server deterministički čita verziju i prilagođava ponašanje contract/DTO sloja. To funkcioniše u lancima proxy-ja i keševa ako se header-i pravilno prenose. Za administratore je takođe dobro proverljivo: zahtev se može reproducirati putem Curl/Postman bez razlike u URL-ovima.

RemObjects SDK nije „REST-puristički“, već pragmatičan service-framework. Upravo zbog toga varijanta sa media-type-om ima smisla: možete zadržati stabilne endpoint-e i istovremeno razvijati ugovore. Važno je da verziju uvek analizirate, centralno odlučite i rezultat prenesete u kontekst vašeg servisa.

Kada varijanta Accept-header-a zakaže?

U praksi postoje tri tipične tačke pucanja koje treba unapred adresirati:

  • Proxy-Policies: Neki reverse proxy-ji ili WAF pravila normalizuju ili filtriraju Accept-header. U tom slučaju vaša API tiho pada na podrazumevanu verziju. Rešenje: eksplicitno proveriti proxy pravila, po potrebi preći na X-Api-Version kao rezervu.
  • Client-Libraries: Neki HTTP-klijenti postavljaju svoje Accept-header-e i prepisuju vrednosti. Rešenje: podržati verziju ugovora i kao opcion parametar query-ja (samo kao fallback), ili server-side tolerantno parsirati Accept-header.
  • Caching: Ако је укључено кеширање одговора, кеш мора да варира по Accept (Vary: Accept), иначе ће испоручивати верзију 1 клијентима верзије 2. Решење: свесно подесити Vary, или онемогућити кеширање на нивоу API-ја.

Извадак кода: Request-Context, Correlation-ID, Version и Error-Mapping

Код је намерно састављен тако да се може интегрисати у постојеће RemObjects сервер-пројекте: мали слој Context-а, парсер за верзију API-ја (из Accept), механизам Correlation-ID и централно мапирање изузетака. Појмови:

  • Correlation-ID: Једнозначан ID по захтеву који се појављује у одговору и на који се позивају логови.
  • Exception-Mapping: Превод интерних Delphi изузетака у стабилне објекте грешака које клијент може обрадити (укљ. HTTP статус).
  • Contract-Version: Верзија JSON уговора која управља понашањем и пољима.
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;
// Очекивано нпр.: 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;
// У продукцији без интерних детаља, без SQL-а, без путева.
// За Debug/Stage то се може проширити преко конфигурације.
begin
  if E is EApiError then
    Exit(E.Message);

  if E is EArgumentException then
    Exit('Невалидни параметри.');

  Exit('Интерна грешка.');
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 zahteva umesto „negde u threadlocal“

Isječak namerno razdvaja: TApiContext je minimalno stanje koje želite proslediti. U RemObjects SDK mnogo toga ide kroz kontekst servera/kanala. U heterogenim projektima (npr. dodatni worker-threadovi, DB-queue, background jobovi) eksplicitno prosleđivanje je često robusnije od implicitnih thread-local promenljivih, jer time ističete konkurentnost i promene konteksta.

Uslovi: Varijanta preko Accept zaglavlja pretpostavlja da vaš reverse proxy (nginx, IIS ARR, Traefik) prosleđuje zaglavlje neizmenjeno. U nekim okruženjima „neobična“ Accept-zaglavlja se filtriraju ili objedine.

Zamke: Verzjonisanje preko Accept je samo toliko pouzdano koliko su vaši testovi. Ako klijenti koriste biblioteke koje prepisuju Accept, API može iznenada pasti na podrazumevanu verziju. Za legacy-klijente podrazumevani fallback ima smisla, ali mora biti vidljiv u monitoring-u (npr. log-warning „Version defaulted“).

Varijante: Ako verzionisanje radije radite preko X-Api-Version: parser je identičan, samo je izvor drugo zaglavlje. Sa stanovišta gateway-a to je ponekad lakše kontrolisati.

Integracija u RemObjects SDK: Correlation-ID i mapiranje izuzetaka na ulazu servisa

Pravu vrednost dobijate kad mehaniku dosledno primenite na ivici vašeg servera: jednom na ulazu zahteva pročitati iz zaglavlja, jednom na izlazu iz izuzetka prevesti u stabilan odgovor. U zavisnosti od hostinga (npr. RO-HTTP-Server, IIS-Hosting, samostalno pokretani Windows-/Windows- und Linux-Services) konkretne tačke za hook se razlikuju; princip ostaje isti: izgraditi kontekst, pozvati business-logiku, centralno mapirati izuzetke.

U RemObjects projektima se često radi direktno po svakoj metodi servisa. To na početku dobro skalira, ali u radu postane problem: svaka metoda gradi logging i obradu grešaka drugačije. čist rez je osnova servisa ili dispatcher koji standardizuje.

Praktičan postupak (svesno kratak i blizak implementaciji)

  1. Pročitati Correlation-ID iz Request-zaglavlja X-Correlation-ID; ako nedostaje, generisati je serverski (npr. GUID).
  2. Pročitati verziju kontrakta iz Accept (ili iz X-Api-Version).
  3. Zabeležiti start zahteva: metoda, putanja, Correlation-ID, udaljena IP, pokrenuti merenje trajanja.
  4. Izvršiti business-logiku; DB-pristupe po mogućstvu enkapsulirati u transakcije.
  5. Uhvatiti izuzetak: odrediti HTTP-status, generisati JSON-objekat greške, postaviti Response-zaglavlje X-Correlation-ID.
  6. Zabeležiti kraj zahteva: status, trajanje, eventualno kod greške.

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

Čest Delphi rubni slučaj: metoda servisa pokreće asinkrone zadatke (npr. generisanje izveštaja, import, push u DMS). U tom slučaju originalni thread zahteva više nije onaj koji kasnije piše logove. Ako je Correlation-ID poznata samo „na početku“, sledljivost se raspada.

Pragmatično pravilo: Sve što ne ostaje striktno u request-threadu, treba dobiti kontekst eksplicitno. Iako to izgleda kao duže liste parametara, isplati se. Alternativno se radi sa jasno definisanim objektom konteksta koji se svesno prosleđuje worker-ima (umesto globalnih promenljivih ili skrivenih singleton-a).

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

  • DB-везе по нити: 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“.
  • Transaktionsgrenzen: Ако у оквиру једног захтева имате више међусобно повезаних корака, транзакција мора остати у истој логичкој јединици. Асинхрони послови не смеју „случајно“ наставити да се извршавају у оквиру исте транзакције.
  • Cancellation: Када клиент прекине везу (Proxy timeout, Browser closed), сервер често настави да ради. Размислите свесно да ли позадински рад тада још увек има смисла.

Приступ подацима и кодови грешака: 409 није „такође 500“

У интеграционим пројектима прецизно мапирање грешака је више од козметике. Одлучује да ли супротна страна (ERP-Connector, ETL-Job, кориснички портал) може правилно реаговати. Неколико практичних смерница које су се показале у Delphi/RemObjects-окружењима:

  • 400 Bad Request: Валидација, недостајући/неважећи параметри, JSON се не може парсирати. Важно: одговор треба остати стабилан, чак и ако је тело захтева оштећено.
  • 401/403: Разликовати аутентификацију и ауторизацију. 401 значи „нема/неважећи идентитет“, 403 „идентитет ок, али забрањено“.
  • 404: Ресурс не постоји. Пазите на безбедност: није увек пожељно откривати да ли нешто постоји.
  • 409 Conflict: Пословни конфликт (нпр. конфликт верзија, „статус не дозвољава ову акцију“, кршење јединственог кључа ако је то релевантно за домен).
  • 422 Unprocessable Content: Када је синтакса у реду, али пословна валидација не пролази (не користе сви тимови 422, али је често јасније од 400).
  • 500: Све што не можете јасно класификовати. Укључује и „DB down“, „Timeout“, „Unhandled Exception“.

Delphi-специфичан трик: Много DB-гршака долази као генерализоване Exceptions. Вреди на слоју приступа подацима циљано проверити познате ситуације и превести их у EApiError. Важно: не преносите SQL-фрагменте или интерне називе табела/колона у поруку ка клијенту. Ти детаљи припадају логу, не одговору.

Трик за дебаговање: репродуцибилне грешке путем „Contract Snapshot“

Необично, али у раду изузетно корисно: при грешкама (или намерно за одређене Correlation-IDs) сачувајте „Snapshot“ са Request-Header и Request-Body у debug-spool фајл. Ово није трајно логовање (заштита података/волумен), већ контролисани алат за репродукцију тешко репродуцибилних случајева из продукције.

Важно: Snapshot никада не сме нефилтрирано перзистирати Auth-Header, токене или личне податке. У пракси то значи: редакција (маскирање) и активирање само преко feature-flag или беле листе (нпр. само за одређене Correlation-IDs, у кратком временском прозору).

Практична примена: маскирање уместо изостављања

У стварним интеграцијама управо „критична“ поља често су она која су потребна за дебаговање (нпр. идентификатори). Уместо општег изостављања боље је маскирати: делимично заменити токене, задржати само домен у адреси е-поште, IBAN задржати само последње цифре. Тако случај остаје репродуцибилан без непотребног ширења података у фајл-систему. Поред тога, snapshot треба јасно означити као debug-артефакт и имати дефинисан рок чувања.

Безбедност и рад: прослеђивање заглавља, Proxy-ланци и таймаути

Једна REST API ретко се завршава непосредно код клијента. Уобичајени су ланци који укључују reverse proxy, TLS-terminaciju, WAF или API-Gateway. Из тога произлазе практични аспекти:

  • Remote IP: Не ослањајте се слепо на X-Forwarded-For. Прихватајте га само од поузданих проксија, у супротном користите директну socket-IP адресу. У оперативним приручницима треба бити наведено који хопови су „trusted“.
  • Timeouts: Ако proxy има 30 секунди, а ваш backend треба 2 минута, изазваћете „ghost-requests“. Поставите timeout-ове конзистентно дуж ланца и одлучите: синхрони захтев или job-pattern (202 Accepted + статус-ендпоинт).
  • Correlation-ID: Поставите Correlation-ID у response-header-е, да администратори могу да је повежу из логова и са клијентске стране. Ако gateway користи сопствене Request-ID-еве: логовати и мапирати обе ID.
  • Fehlertexte: У продукцијском режиму нема унутрашњих детаља. Debug-детаље показивати само контролисано (Stage/Feature-Flag) и, у случају сумње, само у логу.

Einordnung: Warum RemObjects SDK hier im Vorteil sein kann

У Delphi-екосистемима се REST-Server често граде са лакшим framework-овима (нпр. минималистички HTTP-router-и). RemObjects SDK показује своју предност када већ имате или вам треба вишеслојна архитектура:

  • Јасне границе сервиса: сервисне методе су експлицитне, контракте се може верзионисати.
  • Транспорти и сериализација: Можете комуницирати преко JSON-а, али и других формата порука (у зависности од подешавања), без мешања пословне логике.
  • Оперативни рад: Опције hostovanja и интеграција у постојеће Windows- и Linux-Services су планабилне, укључујући чисте rollouts.

Приказани приступ допуњује то деловима који у пракси често недостају: униформни објекти грешака, детерминистичко верзионисање и корелабилно логовање. Управо код индивидуалног корпоративног софтвера са дугим животним циклусом тиме штедите време приликом ажурирања и интеграције екстерних система.

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

Додата вредност се јавља када ваша REST-сичтница није само „ради“, већ је трајно одржива: стабилни JSON-уговори, верзионисање без URL-диваљевања, прегледне грешке и дебаговање без нагађања. Управо ту је приступ са Context, Correlation-ID и централним Exception-Mapping-ом у RemObjects SDK јак.

Границе примене: Ако имате само један, краткотрајан ендпоинт без партнера за интеграцију, Media-Type-verzionisanje брзо делује као overengineering. Исто тако, snapshot-logging има смисла само ако дисциплиновано имплементирате Redaction и активацију. И: ако ваш proxy-stack „оптимизује“ или уклања header-е, прво морате средити инфраструктуру, иначе ћете дебаговати погрешни слој.

Ако модернишете постојећу Delphi-server инфраструктуру или треба да чисто интегришете процесно-близак софтвер у ERP/DMS/CRM, управо ти механизми често представљају разлику између „läuft im Test“ и „läuft im Betrieb“.

У стручном окружењу Delphi REST-API и REST-сервер и Remobjects Sdk Delphi такође имају важну улогу када интеграције, токови података и даљи развој морају да међусобно беспрекорно функционишу.

Разговарајте о пројекту или плану модернизације са Net-Base.

Следећи корак

Када тема прерасте у реалан пројекат, архитектуру, постојеће системе и операције треба рано разматрати заједно.

Подржавамо не само у појединачним питањима, већ и када из исечака изворног кода, застарелих тема или идеја за портале треба да настане поуздан корпоративни пројекат.

  • Постојеће стање, циљано стање и технички ризици оцењују се заједно.
  • REST, приступ подацима, портали и роллаут се неће одлагати као накнадне последице.
  • Ви рано видите који пут је економски и оперативно одржив.

Подели објаву

Поделите ову објаву директно

LinkedIn, X, XING, Facebook, WhatsApp и е-пошта су одмах доступни. За Instagram припремамо линк и кратак текст.

Е-пошта

Инстаграм се отвара у новој картици. Линк и кратак текст се претходно копирају у међуспремник.