Net-Base Revistë

09.06.2026

REST API me RemObjects SDK: versionimi i qartë i endpoint-eve JSON dhe debugimi (Delphi fragment burimi)

Si të ndërtoni me RemObjects SDK në Delphi një API REST që nuk prishet në operim: kontrata JSON të qëndrueshme, versionim pa proliferim të URL-ve, Correlation-ID nëpër të gjitha shtresat, mapim qendror i gabimeve, logim snapshot për rastet e vështira të debugimit dhe udhëzime praktike.

09.06.2026

Nga tema e revistës në praktikën e projektit

Faqe shërbimi dhe teknike të përshtatshme për artikullin

Pse „REST API me RemObjects SDK” në praktikë shpesh përcaktohet nga çështjet periferike

Një REST API me RemObjects SDK rrallë fiton ose humbet te shërbimi „Hello World”, por te pikat ku përplasen operimi, sistemi i trashëguar dhe integrimi: versionimi pa ndërprerje, sjellje e qëndrueshme e gabimeve për të gjitha endpoint-et, debug i riprodhueshëm për zinxhirë proxy dhe aftësia për të korreluar qartë request-et në raste problematike.

RemObjects SDK sjell shumë infrastrukturë për këtë: services, formate mesazhesh, serializim, hosting (p.sh. si Windows- dhe Linux-Services ose pas IIS/Reverse Proxy) dhe pika të përcaktuara për trajtimin qendror të gabimeve. Ajo që shpesh mungon në peizazhe të zhvilluara të softuerit biznesor është një kontratë e zbatuar konsekuent: cilat fusha JSON janë të qëndrueshme? Si sinjalizojmë gabimet? Si e njohim sërish një request kur ai ka kaluar përmes Load Balancer, TLS-Termination dhe disa shtresave backend?

Qasja që vijon (përfshirë snipet nga Delphi) tregon një linjë të fortë për RemObjects SDK: versiononi kontratat JSON, impononi Correlation-ID (Request-ID për gjurmim), përkthejeni Exceptions në HTTP-Status dhe objekte gabimi JSON dhe mos i vendosni debug-un dhe operimin kundër njëri-tjetrit. Shtesë, shqyrtojmë raste kufitare që në ambientet reale shfaqen rregullisht: threading në server, akseset në bazën e të dhënave me BDE-ablomje me lidhje native, header-at e proxy-t, timeouts dhe payload-e klientësh të “ndyra”.

Vendim arkitekturor: Versionimi përmes Media Type në vend të URL

Shumë API versionohen përmes rrugëve si /v1/. Kjo është pragmatike, por në integrime me jetëgjatësi (p.sh. lidhje ERP/DMS/CRM) shpesh çon në duplikim URL-sh, ruterë të dyfishtë, teste të dyfishta dhe pyetjen „Cilin version po përdorim realisht?“ në manualet e operimit.

Një alternativë është versionimi përmes Media Type (Content Negotiation). Klienti dërgon p.sh. Accept: application/vnd.company.order+json;v=2. Shërbimi lexon versionin në mënyrë deterministike dhe përshtat sjelljen e Contract/DTO-ve. Kjo funksionon në zinxhirë proxy dhe cache kur header-at kalohen në mënyrë të pastër. Për administratorët është gjithashtu lehtësisht i verifikueshëm: një request riprodhohet me Curl/Postman pa ndryshim të URL-ve.

RemObjects SDK nuk është „REST-puristisch“, por një framework shërbimesh pragmatik. Pikërisht për këtë ia vlen varianti me Media Type: mund të ruani endpoint-e të qëndrueshme dhe prapë të zhvilloni kontratat. E rëndësishme është që versionin ta lexoni përherë, të vendosni qendrorisht në një vend dhe rezultatin ta transferoni në kontekstin e shërbimit tuaj.

Kur dështon varianti me Accept-Header?

Në praktikë ka tre pika tipike të dobësimit që duhet adresuar paraprakisht:

  • Proxy-Policies: Disa Reverse Proxies/rregulla WAF normalizojnë ose filtrojnë Accept-Header. Atëherë API-ja juaj në heshtje kthehet te vlera default. Zgjidhje: kontrolloni eksplicit rregullat e proxy-t, përndryshe përdorni si alternativë X-Api-Version.
  • Client-Libraries: Disa klientë HTTP vendosin Accept-Header të vet dhe mbishkruajnë vlerat tuaja. Zgjidhje: mbështetni versionin e kontratës edhe si parametër opsional query (vetëm si fallback), ose lejoni që Accept-Header të parsohet në mënyrë tolerante nga ana e serverit.
  • Caching: Nëse përdoret Response-Caching, cache duhet të variojë sipas Accept (Vary: Accept); përndryshe do të kthejë Version 1 te klientët e Version-2. Zgjidhje: vendosni me vetëdije Vary, ose çaktivizoni Caching në nivelin e API-së.
  • Shembull i kodit: Request-Context, Correlation-ID, Version dhe Error-Mapping

    Kodi është projektuar që të integrohet në projektet ekzistuese të serverëve RemObjects: një shtresë e vogël konteksti, një parser për versionin e API-së (nga Accept), një mekanizëm Correlation-ID dhe një mapping qendror i Exceptions. Termat:

    • Correlation-ID: ID unike për çdo request, që shfaqet përsëri në response dhe referohet në log-e.
    • Exception-Mapping: Përkthim i exception-eve të brendshme Delphi në objekte gabimi të qëndrueshme, të përpunueshme nga klienti (përfshirë statusin HTTP).
    • Contract-Version: Versioni i kontratës JSON që përcakton sjelljen dhe fushat.
    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.

    Qëllimi: Kontekst i qëndrueshëm i kërkesës në vend të „diku në Threadlocal“

    Ky snippet ndan qëllimisht: TApiContext është gjendja minimale që dëshironi të kaluar. Në RemObjects SDK shumë gjëra varen nga konteksti Server-/Channel. Në projekte heterogjene (p.sh. worke­r-threads shtesë, DB-queue, punë në sfond) kalimi eksplicit shpesh është më i qëndrueshëm se threadlocals e fshehura, sepse e bën paralelizmin dhe ndërrimin e kontekstit më të dukshëm.

    Kushtet paraprake: Varianti me Accept-Header supozon që Reverse Proxy juaj (nginx, IIS ARR, Traefik) e përcjell header-in pa ndryshime. Në disa ambiente header-at “jo‑të zakonshëm” të Accept mund të filtrohen ose të kombinohen.

    Rreziqet: Versionimi përmes Accept është po aq i mirë sa testet tuaja. Nëse klientët përdorin biblioteka që mbishkruajnë Accept, një API mund të bjerë papritmas në vlerën default. Për klientët legacy, fallback default është i arsyeshëm, por ai duhet të jetë i dukshëm në monitoring (p.sh. warn në log “Version defaulted”).

    Varianta: Nëse preferoni të bëni versionimin përmes X-Api-Version: Parser-i është i njëjtë, ndryshon vetëm burimi i header-it. Nga perspektiva e gateway-ve kjo ndonjëherë është më e lehtë për t’u kontrolluar.

    Integrimi në RemObjects SDK: Correlation-ID dhe Exception‑Mapping në hyrjen e shërbimit

    Efekti real krijohet kur mekanika aplikohet me rreptësi në skajin e serverit tuaj: një herë të lexuar nga header-at në hyrjen e request-it, një herë të përkthehet në një response të qëndrueshëm në daljen nga exception-et. Varësisht nga hosting‑u (p.sh. RO‑HTTP‑Server, IIS‑hosting, shërbime të drejtuara vetë Windows-/Windows- und Linux-Services) pikat konkrete të hook‑ut ndryshojnë; parimi mbetet i njëjtë: ndërtimi i context, thirrja e logjikës së biznesit, mapimi qendror i exception‑eve.

    Në projektet RemObjects shpesh punohet direkt në çdo metodë shërbimi. Kjo skalohet mirë në fillim, por dështon gjatë operimit: çdo metodë ndërton logging dhe trajtim gabimesh në mënyrë të ndryshme. Një prerje e pastër është një baza e shërbimit ose një Dispatcher që standardizon procesin.

    Proces praktik (me qëllim shkurt dhe afër implementimit)

    1. Lexoni Correlation‑ID nga request‑header X-Correlation-ID; nëse mungon, krijojeni server‑sided (p.sh. GUID).
    2. Lexoni Contract‑Version nga Accept (ose nga X-Api-Version).
    3. Logoni fillimin e request‑it: metodën, rrugën, Correlation‑ID, IP‑në remote, nisni matjen e kohës.
    4. Ekzekutoni logjikën e biznesit; kapsuloni akseset në DB sa më shumë të jetë e mundur në mënyrë transaksionale.
    5. Kapni exception‑et: përcaktoni statusin HTTP, krijoni objektin JSON të gabimit, vendosni në response‑header X-Correlation-ID.
    6. Logoni mbylljen e request‑it: statusin, kohën e ekzekutimit, sipas nevojës kodin e gabimit.

    Threading në server: Pse Correlation‑ID pa disiplinë të kontekstit bëhet i papërdorshëm

    Një rast anësor i shpeshtë në serverët Delphi: metoda e shërbimit ndez punë asinkrone (p.sh. gjenerim raporti, import, push në një DMS). Atëherë thread‑i origjinal i request‑it nuk është më ai që në vijim do të shkruajë rreshtat e logut. Nëse Correlation‑ID është i njohur vetëm “në fillim”, trasueshmëria prishet.

    Rregull pragmatic: Çdo gjë që nuk mbetet rreptësisht në request‑thread duhet të marrë kontekstin e kaluar në mënyrë eksplicite. Edhe nëse duket si lista parametrash më e gjatë, vlen të bëhet. Si alternativë mund të punoni me një objekt konteksti të qartë të definuar, i cili i dorëzohet qëllimisht worker‑ëve (në vend të variablave globale ose singleton‑eve të fshehura).

    Pikat tipike kritike në serverët RemObjects-/Delphi:

    • DB-Connections pro Thread: BDE-Ablosung mit nativer Anbindung-Verbindungen nuk ndahen automatikisht në mënyrë të sigurt midis thread-eve. Një Connection-Pool ose një lidhje për çdo thread shpesh është më i përshtatshëm sesa „një lidhje globale“.
    • Kufijtë e transaksioneve: Nëse brenda një request-i keni disa hapa që i përkasin së bashku, transaksioni duhet të qëndrojë brenda të njëjtës njësie logjike. Puna asinkrone nuk duhet të vazhdojë „aksidentalisht“ brenda të njëjtit transaksion.
    • Anullimi: Kur klienti ndërpret (proxy timeout, Browser closed), serveri shpesh vazhdon punën. Mendoni me qëllim nëse puna në sfond ka ende kuptim në atë rast.

    Aksesi në të dhëna dhe kodet e gabimeve: 409 nuk është „gjithashtu një 500“

    Në projekte integrimi, një Error-Mapping i pastër është më shumë se kozmetikë. Ai përcakton nëse pala tjetër (ERP-Connector, ETL-Job, Kundenportal) mund të reagojë si duhet. Disa parime praktike që kanë dhënë rezultate në mjedise Delphi/RemObjects:

    • 400 Bad Request: Validim, parametra të munguar/jopranueshëm, JSON i paparsueshëm. E rëndësishme: përgjigjja duhet të mbetet e qëndrueshme edhe kur body është i dëmtuar.
    • 401/403: Ndani autentifikimin nga autorizimi. 401 do të thotë „identitet i munguar/jovlefshëm“, 403 „identiteti është OK, por i ndaluar“.
    • 404: Burimi nuk ekziston. Kujdes për çështjet e sigurisë: nuk duhet gjithmonë të tregohet nëse diçka ekziston.
    • 409 Conflict: Konflikt funksional (p.sh. konflikt versioni, „statusi nuk lejon këtë veprim“, shkelje e çelësave unikë kur ka rëndësi funksionale).
    • 422 Unprocessable Content: Kur sintaksa është e saktë, por validimi funksional dështon (jo të gjitha ekipet përdorin 422, por shpesh është më i qartë se 400).
    • 500: Gjithçka që nuk mund ta klasifikoni qartë. Këtu përfshihen edhe „DB down“, „Timeout“, „Unhandled Exception“.

    Truk i veçantë në Delphi: Shumë gabime të DB paraqiten si Exceptions generike. Vlen të kontrolloni në shtresën e aksesit në të dhëna për situata të njohura dhe t’i maponi në EApiError. E rëndësishme: Mos e përfshini në mesajin e klientit fragmente SQL ose emra të brendshëm tabelash/kolonash. Këto detaje i përkasin log-ut, jo response-it.

    Truk për Debugging: gabime të riprodhueshme përmes „Contract Snapshot“

    E pazakontë, por jashtëzakonisht e dobishme në operim: ruani tek gabimet (ose në mënyrë selektive për disa Correlation-IDs) një „Snapshot“ të Request-Headern + Request-Body në një skedar debug-spool. Kjo nuk është logging i përhershëm (mbrojtja e të dhënave/volumi), por një mjet i kontrolluar për të riprodhuar raste të vështira afër prodhimit.

    E rëndësishme: Një Snapshot kurrë nuk duhet të persistoje të papërpunuar Auth-Header, Token-e ose të dhëna personale. Në praktikë kjo do të thotë: Redaction (maskim) dhe aktivizim vetëm përmes Feature-Flag ose whitelist (p.sh. vetëm për Correlation-IDs të caktuara, dritare kohe të shkurtra).

    Zbatim i pastër në praktikë: Maskimi në vend të heqjes

    Në integrime reale, fushat „kritike“ shpesh janë ato që ju duhen për debugging (p.sh. identifikatorë). Në vend të heqjes së përgjithshme, është më mirë të maskoni: zëvendësoni pjesërisht token-et, ruani vetëm domain-in e email-it, IBAN vetëm me shifrat e fundit. Kështu rasti mbetet i riprodhueshëm pa shpërndarë të dhëna të panevojshme në sistemin e skedarëve. Për më tepër, Snapshot-i duhet të identifikohet qartë si artefakt debug dhe të ketë një kohë të përcaktuar ruajtjeje.

    Siguria dhe operimi: Përçimi i header-eve, zinxhirët e proxy-ve dhe Timeouts

    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: Mos u mbështetni verbërisht në X-Forwarded-For. Merreni vetëm nga proxy-t e besueshëm dhe përndryshe përdorni IP-në direkte të socket-it. Në manualet e operimit duhet të përcaktohet cilët hops „trusted“ sind.
    • Timeouts: Nëse proxy ka 30 sekonda, por backend-i juaj kërkon 2 minuta, krijoni Ghost-Requests. Përcaktoni Timeouts në mënyrë konsistente përgjatë gjithë zinxhirit dhe vendosni: kërkesë sinkrone apo Job-Pattern (202 Accepted + Status-Endpunkt).
    • Correlation-ID: Vendosni Correlation-ID në Response-Header, në mënyrë që administratorët ta bashkojnë atë nga log-et dhe ana e klientit. Nëse një gateway përdor Request-ID të veta: log-oni dhe map-oni të dy ID-të.
    • Fehlertexte: Në prodhim asnjëherë detaje të brendshme. Debug-details vetëm të kontrolluara (Stage/Feature-Flag) dhe, në rast dyshimi, vetëm në log.

    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: Metodat e shërbimit janë eksplizite, kontraktet janë të versionueshme.
    • Transporte und Serialisierung: Mund të flisni JSON, por edhe formate të tjera mesazhesh (sipas konfigurimit), pa ndërluar logjikën e biznesit.
    • Betrieb: Opsionet e hosting dhe integrimi në Windows- und Linux-Services janë të planifikueshme, përfshirë rollout-e të pastra.

    Qasja e paraqitur plotëson pjesët që shpesh mungojnë në përditshmëri: objekte të njëformëta gabimesh, versionim deterministik dhe logging i korrelueshëm. Veçanërisht në software-in individual të ndërmarrjes me cikle të gjata jetësore, kurseni kohë në update-e dhe në integrimin e sistemeve të jashtme.

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

    Vlera shtesë shfaqet kur ndërfaqja juaj REST nuk vetëm që “funksionon”, por është e qëndrueshme në operim: kontrata JSON të qëndrueshme, versionim pa rrëmujë URL-sh, gabime të dokumentueshme dhe debug pa lojëra me supozime. Pikërisht aty qasja me kontekst, Correlation-ID dhe mapim qendror të përjashtimeve në RemObjects SDK është e fortë.

    Einsatzgrenzen: Nëse keni vetëm një endpoint të vetëm, afatshkurtër dhe pa partnerë integrimi, Media-Type-Versionierung shpejt duket si Overengineering. Edhe Snapshot-Logging ka kuptim vetëm nëse implementoni me disiplinë Redaction dhe aktivizim. Dhe: Nëse staku juaj i proxy-ve “optimiert” ose heq header-at, duhet së pari të rregulloni infrastrukturën, përndryshe do të debug-oni shtresën e gabuar.

    Kur modernizoni një peizazh ekzistues serverash Delphi ose duhet të integroni pastër një zgjidhje softuerike afër procesit në ERP/DMS/CRM, shpesh këto mekanizma janë ndryshimi midis “funksionon në test” dhe “funksionon në prodhim”.

    Në kontekstin profesional luajnë gjithashtu një rol të rëndësishëm Delphi REST-API dhe REST-Server dhe Remobjects Sdk Delphi, kur integrimet, rrjedhat e të dhënave dhe zhvillimi i mëtejshëm duhet të bashkëveprojnë në mënyrë të strukturuar.

    Diskutoni projektin ose iniciativën e modernizimit me Net-Base.

    Hapi tjetër

    Kur nga një temë bëhet një projekt real, arkitektura, sistemi ekzistues dhe operimi duhet të merren në konsideratë së bashku që në fazat e hershme.

    Ne nuk mbështesim vetëm në çështje të veçanta, por edhe kur nga fragmente të kodit burimor, temat legacy ose idetë për portale duhet të zhvillohen në një projekt korporativ të qëndrueshëm.

    • Gjendja ekzistuese, imazhi i synuar dhe rreziqet teknike vlerësohen së bashku.
    • REST, akses në të dhëna, portalet dhe Rollout nuk shtyhen si pasoja të mëvonshme.
    • Ju e shihni herët se cila rrugë është e qëndrueshme ekonomikisht dhe operativisht.

    Ndaje postimin

    Shpërndaj këtë postim drejtpërdrejt

    LinkedIn, X, XING, Facebook, WhatsApp dhe E‑Mail janë menjëherë të disponueshme. Për Instagram po përgatitim menjëherë lidhjen dhe tekstin e shkurtër.

    Postë elektronike

    Instagram hapet në një skedë të re. Linku dhe teksti i shkurtër kopjohen më parë në memorjen e kopjimit.