Net-Base Magasin

09.06.2026

REST API med RemObjects SDK: JSON-endepunkt — ryddig versjonering og feilsøking (Delphi kildeutdrag)

Korleis du med RemObjects SDK i Delphi byggjer opp ein REST API som ikkje bryt saman i drift: stabile JSON-kontraktar, versjonering utan URL-rot, Correlation-ID gjennom alle lag, sentralt Error-Mapping, Snapshot-Logging for krevjande feilsøkingstilfelle samt praktiske råd.

09.06.2026

Frå magasinetema til prosjektpraksis

Passande teneste- og tekniske sider til innlegget

Kvifor „REST API med RemObjects SDK“ i praksis ofte avgjer ved ytterkantane

Ein REST API med RemObjects SDK står og fell sjeldan på „Hello World“-tenesta, men på stadene der drift, legacy og integrasjon kolliderer: versjonshandtering utan nedetid, konsistent feilhandsaming over alle endepunkt, reproducerbar debugging ved proxy-kjeder og evna til å korrelere requestar entydig når det oppstår problem.

RemObjects SDK leverer mykje infrastruktur: tenester, meldingsformat, serialisering, hosting (t.d. som Windows- og Linux-Services eller bak IIS/Reverse Proxy) og definerte punkt for sentral feilhandsaming. Det som ofte manglar i modne business-software-landskap, er ein konsekvent gjennomført kontrakt: Kva JSON-felt er stabile? Korleis signaliserer vi feil? Korleis gjenkjenner vi ein request når han har gått gjennom load balancer, TLS-terminering og fleire backend-lag?

Følgjande tilnærming (inkludert Delphi-snipsel) viser ein robust linje for RemObjects SDK: Versjonere JSON-kontraktar, krevje Correlation-ID (Request-ID for sporing), oversette Exceptions til HTTP-status og JSON-feilobjekt og ivareta både debugging og drift. I tillegg ser vi på randtilfelle som ofte oppstår i reelle miljø: threading i server, databasenøklings med BDE-avløysing med nativer bindingar, proxy-header, timeouts og „skitne“ klientpayloadar.

Arkitektur-avgjerd: Versjonering via mediatype i staden for URL

Fleire API-ar versjonerer via stiar som /v1/. Det er pragmatisk, men i langlevde integrasjonar (t.d. ERP/DMS/CRM-tilkoplingar) fører det ofte til duplisering av URL-ar, doble rutar, dobbel testing og „kva versjon brukar vi eigentleg?“ i driftsdokumentasjonen.

Eit alternativ er versjonering via mediatype (innhaldsforhandling). Klienten sender til dømes Accept: application/vnd.company.order+json;v=2. Serveren les versjonen deterministisk og tilpassar Contract/DTO-åtferda. Dette fungerer i proxy- og cache-kjeder, gitt at headerane vert vidareført korrekt. For administratorar er det òg lett å kontrollere: Ein request kan reproduserast med Curl/Postman utan at URL-ane skil seg.

RemObjects SDK er ikkje „REST-puristisk“, men eit pragmatisk service-rammeverk. Nøyaktig difor løyser mediatype-varianten seg: De kan behalde stabile endepunkt og likevel vidareutvikle kontraktar. Viktig er at De alltid les versjonen, avgjer sentralt og tek resultatet inn i service-konteksten.

Når sviktar Accept-header-varianten?

I praksis finst det tre typiske sviktpunkt som bør adresserast på førehand:

  • Proxy-reglar: Nokre Reverse Proxies/WAF-reglar normaliserer eller filtrerer Accept-headeren. Då fell API-en dykkar stille tilbake til default. Løysing: Kontroller proxy-reglane eksplisitt, eventuelt ty til X-Api-Version.
  • Klientbibliotek: Nokre HTTP-klientar set eigne Accept-headerar og overskriv verdiar. Løysing: Støtt kontraktsversjon også som ein valfri query-parameter (berre som fallback), eller parsa Accept-headeren tolerantly på serversida.
  • Caching: Dersom Response-Caching er i bruk, må cachen variere etter Accept (Vary: Accept), ellers leverer han versjon 1 til versjon-2-klientar. Løysing: sett Vary med vilje, eller deaktiver caching på API-nivå.
  • Kodeutdrag: Request-Context, Correlation-ID, Version og Error-Mapping

    Koden er med vilje utforma slik at han kan integrerast i eksisterande RemObjects-serverprosjekt: eit lite kontekstlag, ein parser for API-versjonen (frå Accept), ein Correlation-ID-mekanisme og eit sentralt Exception-Mapping. Omgrep:

    • Correlation-ID: Eit eintydig ID per Request som dukkar opp att i Response og vert referert i loggar.
    • Exception-Mapping: Omsetjing av interne Delphi-Exceptions til stabile, klienthandsamlege feilobjekt (inkl. HTTP-status).
    • Contract-Version: Versjon av JSON-kontrakten som styrer oppførsel og felt.
    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;
    // Forventar t.d.: 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;
    // I drift ingen interne detaljar, ingen SQL, inga filstiar.
    // For debug/stage kan ein utvide dette via konfigurasjon.
    begin
      if E is EApiError then
        Exit(E.Message);
    
      if E is EArgumentException then
        Exit('Ugyldige parameter.');
    
      Exit('Intern feil.');
    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.

    Formål: Stabil request-kontekst i staden for „irgendwo im Threadlocal“

    Utdraget skil medvite: TApiContext er den minimale tilstanden som skal sendast vidare. I RemObjects SDK går mykje via server-/channel-kontekst. I heterogene prosjekt (t.d. ekstra Worker-Threads, DB-Queue, bakgrunnsjobbar) er eksplisitt vidareføring ofte meir robust enn implisitte Threadlocals, fordi det gjer samtid og kontekstskifte meir synleg.

    Forutsetningar: Accept-Header-varianten forutset at reverse-proxyen dykkar (nginx, IIS ARR, Traefik) vidareformidlar headeren uendra. I enkelte miljø blir «uvanlege» Accept-header filtrert eller slått saman.

    Fallgruver: Versjonering via Accept er berre så god som testane dine. Dersom klientbibliotek overskriv Accept, kan ei API plutseleg falle tilbake til default. For legacy-klientar er ein standardfallback fornuftig, men han må vere synleg i overvakinga (t.d. logg-varsel „Version defaulted“).

    Variantar: Om ein heller vil gjere versjonering via X-Api-Version: Parsaren er identisk, berre kjelda er ein annan header. Frå gateway-synspunkt er det av og til enklare å kontrollere.

    Integrasjon i RemObjects SDK: Correlation-ID og Exception-Mapping ved tenesteinngang

    Den reelle effekten kjem når ein brukar mekanikken konsekvent i kanten av serveren: ein gong ved request-oppstart lese frå headerar, ein gong ved exception-uttak oversette til ein stabil response. Avhengig av hosting (t.d. RO-HTTP-Server, IIS-hosting, eigndrifta Windows-/Windows- og Linux-Services) varierer dei konkrete hook-punkta; prinsippet er det same: bygg kontekst, kall forretningslogikk, map unntak sentralt.

    I RemObjects-prosjekt blir det ofte jobba direkte per service-metode. Det skalerer godt i starten, men sviktar i drift: Kvar metode byggjer logging og feilhandtering ulikt. Ein rein snitt er ein Service-Basis eller ein Dispatcher som standardiserer.

    Praktisk framgangsmåte (medvite kort og implementeringsnær)

    1. Les Correlation-ID frå request-header X-Correlation-ID; dersom ho manglar, generer server-sida (t.d. GUID).
    2. Les kontraktsversjon frå Accept (eller frå X-Api-Version).
    3. Logg request-start: metode, sti, Correlation-ID, fjern-IP, start tidsmåling.
    4. Kjør forretningslogikk; kapsle DB-tilgang helst transaksjonelt.
    5. Fang unntak: bestem HTTP-status, lag JSON-feilobjekt, sett response-header X-Correlation-ID.
    6. Logg request-slutt: status, varigheit, eventuelt feilkode.

    Trådbruk i serveren: Kvifor Correlation-ID utan kontekstdisiplin blir verdilaus

    Eit vanleg Delphi-kanttilfelle: Tenestemetoden triggar asynkrone oppgåver (t.d. rapportgenerering, import, push til eit DMS). Då er ikkje den opphavlege request-tråden lenger den som seinare skriv logglinjene. Dersom Correlation-ID berre er kjend „i starten“, fell sporbarheiten bort.

    Pragmatisk regel: Alt som ikkje blir strikt i request-tråden, får konteksten eksplisitt overlevert. Sjølv om det ser ut som fleire parameterar, løner det seg. Alternativt kan ein bruke eit klart definert kontekst-objekt som medvite blir sendt til workerar (istadenfor globale variablar eller skjulte Singletons).

    Typiske vippepunkt i RemObjects-/Delphi-serverar:

    • DB-Connections pro Thread: DB-tilkoblingar er ikkje automatisk trygt delbare mellom trådar. Ein Connection-pool eller ei tilkobling per tråd er ofte meir fornuftig enn ein «globale Connection».
    • Transaktionsgrenzen: Når du har fleire steg innanfor ein request som høyrer saman, må transaksjonen halde seg innanfor same logiske eining. Asynkront arbeid må ikkje «ved eit uhell» fortsette i same transaksjon.
    • Cancellation: Når klienten avbryt (proxy-timeout, nettlesar lukka), held serveren ofte fram å kjøre. Vurder medvite om bakgrunnsarbeid då framleis gir meining.

    Datenzugriff und Fehlercodes: 409 ist nicht „auch ein 500“

    I integrasjonsprosjekt er ryddig feilmapping meir enn kosmetikk. Det avgjer om ein motpart (ERP-Connector, ETL-jobb, Kundenportal) kan reagere korrekt. Nokre praksisnære rettesnorer som har vist seg i Delphi/RemObjects-omgjevnader:

    • 400 Bad Request: Validering, manglande/ugyldige parameter, JSON ikkje parsebar. Viktig: Svaret skal vere stabilt, sjølv om body er øydelagd.
    • 401/403: Skil mellom autentisering og autorisasjon. 401 betyr «ingen/ugyldig identitet», 403 «identitet ok, men forbode».
    • 404: Ressurs finst ikkje. Ver varsam med sikkerheit: Ikkje alltid avslør om noko finst.
    • 409 Conflict: Fagleg konflikt (t.d. versjonskonflikt, «status tillèt ikkje denne handlinga», unike nøkkelbrot når det er fagleg relevant).
    • 422 Unprocessable Content: Når syntaksen er ok, men fagleg validering feilar (ikkje alle team bruker 422, men det er ofte klarare enn 400).
    • 500: Alt som du ikkje kan klassifisere presist. Dette inkluderer òg «DB down», «Timeout», «Unhandled Exception».

    Delphi-spesifikt knep: Mange DB-feil kjem opp som generiske Exceptions. Det løner seg å sjekke kjente situasjonar i datatilgangslaget og overføre dei til EApiError. Viktig her: Ta ikkje med SQL-fragment eller interne tabell-/kolonnenamn i klientmeldinga. Desse detaljane høyrer i loggen, ikkje i response.

    Debugging-Kniff: reproduzierbare Fehler durch „Contract Snapshot“

    Uvanleg, men i drift svært nyttig: Lagre ved feil (eller målretta for bestemte Correlation-IDs) ein «Snapshot» frå Request-Headern + Request-Body i ei debug-spool-fil. Dette er ikkje permanent logging (personvern/volum), men eit kontrollert verkty for å gjenskape vanskeleg reproducerbare tilfelle frå produksjon.

    Viktig: Ein snapshot må aldri persistere ufiltret Auth-Header, token eller personopplysningar. I praksis betyr det: Redaction (Maskierung) og aktivering berre via feature-flag eller whitelist (t.d. berre for bestemte Correlation-IDs, korte tidsvindauge).

    Saubere Umsetzung in der Praxis: Maskieren statt Weglassen

    I reelle integrasjonar er ofte nett dei «kritiske» felta dei ein treng for debugging (t.d. identifikatorar). I staden for generelt å fjerne, er maskering betre: Erstatt delar av token, behald berre domenet i e-postadresse, IBAN berre dei siste siffera. Slik blir feilen framleis reproducerbar utan å spreie unødvendige data i filsystemet. I tillegg bør snapshotet vere tydeleg merka som eit debug-artefakt og ha ei definert oppbevaringstid.

    Sikkerheit og drift: Header-vidareføring, proxy-kjeder og Timeouts

    Ei REST API endar sjeldan direkte ved klienten. Vanleg er kjeder av reverse proxy, TLS-terminering, WAF eller API-gateway. Frå dette følgjer praktiske punkt:

    • Fjern-IP: Stol ikkje blindt på X-Forwarded-For. Ta berre verdien frå pålitelege proxya, elles bruk den direkte socket-IP-en. I driftsmanualar bør det stå kva hops som er «trusted».
    • Timeouts: Hvis proxyen har 30 sekund, men backendet ditt treng 2 minutt, skapar du ghost-requests. Set Timeouts konsistent langs kjeda og avgjer: synkron request eller Job-Pattern (202 Accepted + status-endepunkt).
    • Correlation-ID: Set Correlation-ID-en i response-headerane, slik at administratorar kan slå ho saman frå loggar og klient-side. Dersom eit gateway brukar eigne request-IDar: logg og kartlegg begge ID-ane.
    • Feiltekst: I produksjon ingen interne detaljar. Debug-detaljar berre kontrollert (Stage/Feature-Flag) og ved tvil berre i loggen.

    Innordning: Kvifor RemObjects SDK kan vere ein fordel her

    I Delphi-økosystem blir REST-Server ofte bygd med lettare rammeverk (t.d. minimalistiske HTTP-router). RemObjects SDK spelar sine styrker ut når du allereie har eller treng ein fleirlagsarkitektur:

    • Klare tenestegrenser: Tenestemetodar er eksplisitte, kontraktar er versjonerbare.
    • Transport og serialisering: Du kan bruke JSON, men òg andre meldingsformat (avhengig av oppsett), utan å blande forretningslogikken.
    • Drift: Hosting-opsjonar og integrasjon i eksisterande Windows- og Linux-Services kan planleggast, inkludert ryddige utrullingar.

    Den viste tilnærminga kompletterer dette med dei delane som ofte manglar i kvardagen: einheitlege feilobjekt, deterministisk versjonering og korrelerbart logging. Særleg for individuell bedriftsprogramvare med lange livssyklusar sparar du tid ved oppdateringar og ved integrasjon av eksterne system.

    Konklusjon: Løner innsatsen seg — og når blir tilnærminga for krevjande?

    Mervarden oppstår når ditt REST-grensesnitt ikkje berre «fungerer», men er varig driftbart: stabile JSON-kontraktar, versjonering utan URL-villvoksing, ettersporbare feil og debugging utan gjetjing. Nett der er tilnærminga med Context, Correlation-ID og sentralt Exception-Mapping i RemObjects SDK sterk.

    Bruksgrenser: Dersom du berre har eitt enkelt, kortvarig endepunkt utan integrasjonspartnarar, verkar Media-Type-Versionierung fort som Overengineering. Også Snapshot-Logging er berre fornuftig viss du disiplinert implementerer Redaction og Aktivering. Og: Dersom proxy-stacken din headerar «optimaliserer» eller fjernar, må du fyrst rette opp infrastrukturen, elles feilsøker du i feil lag.

    Når du moderniserer ein eksisterande Delphi-serverlandskap eller må integrere ei prosessnær programvareløysing ryddig i ERP/DMS/CRM, er nett desse mekanismane ofte skilnaden mellom «køyrer i test» og «køyrer i drift».

    I fagleg samanheng spelar også Delphi REST-API og REST-Server og Remobjects Sdk Delphi ei viktig rolle, når integrasjonar, dataflyt og vidareutvikling må fungere sømløst saman.

    Drøft prosjekt eller moderniseringstiltak med Net-Base.

    neste steg

    Når temaet blir eit reelt prosjekt, bør arkitektur, eksisterande system og drift tidleg saman vurderast.

    Vi støttar ikkje berre ved enkeltspørsmål, men òg når korte kildekodesnuttar, legacy-tema eller portalidéar skal utviklast til eit robust bedriftsprosjekt.

    • Eksisterande tilstand, målbiletet og tekniske risikoar blir vurderast samla.
    • REST, datatilgang, portalar og utrulling blir ikkje utsett til seinare fasar.
    • De ser tidleg kva veg som er økonomisk og driftsmessig berekraftig.

    Del innlegg

    Del dette innlegget direkte

    LinkedIn, X, XING, Facebook, WhatsApp og e-post er straks tilgjengelege. For Instagram klargjer vi lenke og kort tekst med det same.

    E-post

    Instagram opnar i ein ny fane. Lenkje og kort tekst blir kopiert til utklippstavla på førehand.