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 mit RemObjects SDK“ i praksis ofte avgjer på randane

Ein REST API mit RemObjects SDK står og fell sjeldan på «Hello World»-servicen, men på stadene der drift, legacy og integrasjon kolliderer: versjonshandtering utan stopp, konsekvent feilhandsaming over alle endepunkt, reproducerbar feilsøking gjennom proxy-kjeder og evna til entydig korrelasjon av requests ved problem.

RemObjects SDK leverer mykje infrastruktur for dette: tenester, meldingsformat, serialisering, hosting (t.d. som Windows- und Linux-Services eller bak IIS/Reverse Proxy) og definerte punkt for sentral feilhandsaming. Det som i etablerte business-software-landskap ofte manglar, er ein konsekvent gjennomført kontrakt: Kva JSON-felt er stabile? Korleis signaliserer vi feilar? Korleis kjenner vi igjen ein request etter at han har passert load balancer, TLS-terminering og fleire backend-lag?

Følgjande tilnærming (inkludert Delphi-snipsel) skisserer ei robust linje for RemObjects SDK: JSON-kontraktar versjonerast, Correlation-ID (Request-ID for sporing) krevjast, Exceptions i HTTP-status og JSON-feilobjekt omsetjast, samtidig som feilsøking og drift ikkje vert stilt opp mot kvarandre. I tillegg ser vi på randtilfelle som i reelle miljø jamleg opptrer: threading på server, database-tilgang ved BDE-avløysing med nativer bindingar, proxy-headarar, timeouts og «skitne» klient-payloads.

Arkitekturvedtak: Versjonering via mediatype i staden for URL

Mange API-ar versjonerer gjennom stiar som /v1/. Det er pragmatisk, men i langtlevande integrasjonar (t.d. ERP/DMS/CRM-tilkoplingar) fører det ofte til duplisering av URL-ar, doble ruter, dobbel testing og spørsmålet «Kva versjon brukar vi eigentleg?» i driftsdokumentasjonen.

Eit alternativ er versjonering via mediatype (content negotiation). Klienten sender t.d. Accept: application/vnd.company.order+json;v=2. Serveren les versjonen deterministisk og tilpassar kontrakt/DTO-oppførsel tilsvarande. Dette fungerer i proxy- og cache-kjeder når headerane blir vidareført korrekt. For administratorar er det dessutan lett å verifisere: ein request kan reproducerast med Curl/Postman utan at URL-ane skil seg frå kvarandre.

RemObjects SDK er ikkje «REST-puristisk», men eit pragmatisk service-rammeverk. Nett av den grunn løner mediametype-varianten seg: De kan behalde stabile endepunkt og likevel utvikle kontraktar vidare. Viktig er at de alltid evaluerer versjonen, avgjer sentralt eitt stader og tek resultatet inn i service-konteksten.

Kven rådner Accept-header-varianten?

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

  • Proxy-policyar: Nokre reverse-proxyar/WAF-reglar normaliserer eller filtrerer Accept-header. Då fell API-et stille tilbake til standard. Løysing: sjekk proxy-reglane eksplisitt, eventuelt fall tilbake på X-Api-Version.
  • Client-bibliotek: Enkelte HTTP-klientar set eigne Accept-headerar og overskriv verdiar. Løysing: støtt kontraktsversjon også som ein valfri query-parameter (berre som fallback), eller parse Accept-headeren server-side med tolerant logikk.
  • Caching: Når Response-Caching er i bruk, må cachen variere etter Accept (Vary: Accept), elles leverer han versjon 1 til versjon-2-klientar. Løysing: sett Vary medvite, eller deaktiver caching på API-nivå.

Kildeutdrag: Request-Context, Correlation-ID, Versjon og Error-Mapping

Koden er med vilje skoren slik at han let seg integrere i eksisterande RemObjects-serverprosjekt: eit lite Context-lag, ein parser for API-versjonen (frå Accept), ein Correlation-ID-mekanisme og eit sentralt Exception-Mapping. Omgrep:

  • Correlation-ID: Entydig ID per request som går att i response og som blir referert i loggar.
  • Exception-Mapping: Omsetjing av interne Delphi-Exceptions til stabile, klientbehandlingslege feilobjekt (inkl. HTTP-Status).
  • Contract-Version: Versjon av JSON-kontrakten som styrer åtferd 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;
// 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.

Formål: Stabil request-kontekst i staden for „ein eller annan Threadlocal”

Snutten skil medvite: TApiContext er den minimale tilstanden du vil sende med. I RemObjects SDK går mykje gjennom server-/channel-kontekst. I heterogene prosjekt (til dømes ekstra Worker-Threads, DB-Queue, bakgrunnsjobbar) er eksplisitt gjennompassing oftare meir robust enn implisitte Threadlocals, fordi du med det gjer samtid og kontekstskifte meir synlege.

Forutsetningar: Accept-header-varianten krev at Reverse Proxy (nginx, IIS ARR, Traefik) vidareformidlar headeren uendra. I nokre miljø blir «uvanlege» Accept-headerar filtrerte eller samla saman.

Fallgruver: Versjonering via Accept er berre så god som testane de har. Om klientbibliotek overskriver Accept, kan ei API plutseleg falle tilbake til default. For legacy-klientar er eit default-fallback fornuftig, men det må vere synleg i overvakinga (t.d. loggvarsel «Version defaulted»).

Variantar: Om de føretrekk å gjere versjonering via X-Api-Version: parseren 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 serviceinngang

Den faktiske verknaden kjem når de brukar mekanikken konsekvent i utkanten av serveren: ein gong ved request-inngang lese frå headerar, ein gong ved exception-utfall omsetje til ein stabil response. Avhengig av hosting (til dømes RO-HTTP-Server, IIS-hosting, sjølvdriven Windows-/Windows- og Linux-Services) varierer dei konkrete hook-punkta; prinsippet er det same: bygg Context, kall forretningslogikk, map Exceptions sentralt.

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

Praktisk framgangsmåte (medvite kort og implementasjonsnært)

  1. Les Correlation-ID frå request-header X-Correlation-ID; om ho manglar, generer ho server-side (t.d. GUID).
  2. Les kontraktversjon frå Accept (eller frå X-Api-Version).
  3. Logg request-start: metode, sti, Correlation-ID, remote IP; start varigheitsmåling.
  4. Utfør forretningslogikk; kapsle DB-tilgang helst transaksjonelt.
  5. Fang Exceptions: bestem HTTP-status, bygg eit JSON-feilobjekt, sett response-header X-Correlation-ID.
  6. Logg request-slutt: status, varigheit, eventuelt feilkode.

Trådhandtering i serveren: Kvifor Correlation-ID utan kontekst-disiplin blir verdilaus

Eit vanleg Delphi-kanttilfelle: Servicemetoda triggar asynkront arbeid (til dømes rapportgenerering, import, push til eit DMS). Då er ikkje lenger den opprinnelege request-tråden den som skriv logglinjer seinare. Om Correlation-ID berre er kjend «i starten», fell sporbarheita frå kvarandre.

Pragmatiske regel: Alt som ikkje blir verande strengt i request-tråden, skal få Context eksplisitt overlevert. Sjølv om det ser ut som fleire parameterlister, løner det seg. Alternativt kan ein bruke eit klart definert kontekstobjekt som medvite blir sendt til workerar (i staden for globale variablar eller skjulte singletons).

Typiske vippepunkt i RemObjects-/Delphi-serverar:

  • DB-tilkoplingar per tråd: BDE-Ablosung mit nativer Anbindung-tilkoplingar er ikkje automatisk trygt delbare mellom trådar. Ein tilkoplingspool eller ein tilkopling per tråd er ofte meir fornuftig enn «ein global tilkopling».
  • Transaksjonsgrenser: Når ein innanfor eit request har fleire steg som høyrer saman, må transaksjonen halde seg innan same logiske eining. Asynkron arbeid må ikkje «ved eit uhell» vidaregå i same transaksjon.
  • Avbryting: Når klienten avbryt (proxy-timeout, nettlesar lukka), vil serveren ofte halde fram. Vurder medvite om bakgrunnsarbeid då framleis gir meining.

Dataåtkomst og feilkodar: 409 er ikkje «også ein 500»

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

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

Delphi-spesifikk tilnærming: Mange DB-feil dukkar opp som generiske Exceptions. Det løner seg å på dataåtkomstlaget målretta sjekke for kjende situasjonar og mappe dei til EApiError. Viktig: Ikkje ta med SQL-fragment eller interne tabell-/kolonnenamn i klientmeldinga. Desse detaljane høyrer i loggen, ikkje i responsen.

Debugging-knep: reproducerbare feil via «Contract Snapshot»

Uvanleg, men i drift ekstremt nyttig: Lagre ved feil (eller målretta for bestemte Correlation-IDs) ein «Snapshot» av 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 ufiltrerte Auth-Header, Tokens eller personopplysningar. I praksis betyr det: Redaction (maskering) og aktivering berre via feature-flag eller whitelist (t.d. berre for bestemte Correlation-IDs, korte tidsvindauge).

Ryddig gjennomføring i praksis: Maskering framfor utelating

I reelle integrasjonar er nettopp dei «kritiske» felta ofte dei ein treng for debugging (t.d. identifikatorar). I staden for generell utelating er maskering betre: delvis erstatte token, behalde berre domenet i e-postadresse, IBAN berre dei siste sifrene. Slik held saka seg reproducerbar utan å spreie unødvendige data i filsystemet. I tillegg bør Snapshotet vere klart merka som eit debug-artefakt og ha definert oppbevaringstid.

Sikkerheit og drift: videresending av header, proxy-kjeder og timeouts

Ein REST API endar sjeldan direkte ved klienten. Typisk ligg det ein kjede av reverse proxy, TLS-terminering, WAF eller API-gateway i mellom. Dette gir praktiske følgjer:

  • Fjern-IP: Ikkje stol blindt på X-Forwarded-For. Ta berre imot denne frå pålitelege proxyar, elles bruk den direkte socket-IP-en. I driftsmanualar bør det stå kva hopp som er pålitelege („trusted“).
  • Timeouts: Har proxyen 30 sekund og backendet ditt treng 2 minutt, skapar du ghost-requests. Sett timeouts konsistent langs kjeda og avgjer om de skal køyre synkrone request eller nyttar eit jobb-mønster (202 Accepted + status-endepunkt).
  • Correlation-ID: Setj Correlation-ID i response-headerar, slik at administratorar kan knyte saman loggar og klientside. Dersom eit gateway nyttar eigne request-ID-ar: logg begge ID-ane og kartlegg samspelet.
  • Feilmeldingar: I produksjon ingen interne detaljar i feilmeldingane. Debugdetaljar berre kontrollert (stage/feature-flag) og i tvil berre i logg.

Vurdering: Kvifor RemObjects SDK kan vere ein fordel her

I Delphi-økosystem blir REST-serverar ofte bygd med lettare rammeverk (t.d. minimalistiske HTTP-routerar). RemObjects SDK spelar på si styrke når de allereie har eller treng ei fleirlagsarkitektur:

  • Tydelege servicegrenser: Servicemetodar er eksplisitte, kontraktar kan versjonerast.
  • Transports og serialisering: De kan kommunisere via JSON, men òg andre meldingformat avhengig av oppsett, utan å blande forretningslogikk og transport.
  • Drift: Hosting-alternativ og integrasjon i eksisterande Windows- og Linux-tenester er planbar, inklusive ryddige rollout-prosessar.

Den viste tilnærminga kompletterer dette med dei delane som ofte manglar i kvardagen: einsarta feilmåte, deterministisk versjonshandtering og korrelerbart logging. Særleg for individuell bedriftsprogramvare med lange livsløp sparar dette tid ved oppdateringar og ved integrasjon av eksterne system.

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

Meirverdien kjem når din REST-grensesnitt ikkje berre „fungerer“, men er driftbar over tid: stabile JSON-kontraktar, versjonshandtering utan URL-rot, etterprøvbare feil og debugging utan gjetting. Det er her tilnærminga med context, Correlation-ID og sentralt exception-mapping i RemObjects SDK er solid.

Eigenskapar og begrensingar: Om de berre har eit enkelt, kortlivet endepunkt utan integrasjonspartnarar, kan Media-Type-versionering raskt vere overengineering. Også Snapshot-logging gir berre meining dersom de disiplinert implementerer redaction og aktivering. Og: om proxy-stacken dykkar «optimaliserer» eller fjerner headerar, må de retta opp infrastrukturen først — elles endar de opp med å debugge feil lag.

Når de moderniserer ein eksisterande Delphi-serverlandskap eller skal integrere ei prosessnær løysing ryddig inn i ERP/DMS/CRM, er ofte nett desse mekanismane skilnaden mellom „kjører i test“ og „kjører i produksjon“.

I det faglege miljøet spelar òg Delphi REST-API og REST-Server og Remobjects Sdk Delphi ei viktig rolle når integrasjonar, dataflyt og vidareutvikling må spela godt saman.

Drøfte prosjekt eller moderniseringsprosjekt med Net-Base.

Neste steg

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

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 utsette til seinare som etterverknader.
  • 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 tilgjengelege med ein gong. 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.