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.
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.
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)
- Les Correlation-ID frå request-header
X-Correlation-ID; dersom ho manglar, generer server-sida (t.d. GUID). - Les kontraktsversjon frå
Accept(eller fråX-Api-Version). - Logg request-start: metode, sti, Correlation-ID, fjern-IP, start tidsmåling.
- Kjør forretningslogikk; kapsle DB-tilgang helst transaksjonelt.
- Fang unntak: bestem HTTP-status, lag JSON-feilobjekt, sett response-header
X-Correlation-ID. - 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.
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.