Net-Base Revista

09.06.2026

REST API amb RemObjects SDK: versionar i depurar endpoints JSON de manera coherent (Delphi fragment de codi)

Com construir amb RemObjects SDK a Delphi una REST API que no es trenqui en funcionament: contractes JSON estables, versionat sense proliferació descontrolada d'URLs, Correlation-ID a través de totes les capes, mapeig central d'errors, registre d'instantànies per a casos difícils de depuració així com indicacions pràctiques...

09.06.2026

Del tema de la revista a la pràctica del projecte

Pàgines de serveis i tècniques pertinents per a l'article

Per què una «REST API amb RemObjects SDK» sovint decideix a la pràctica en els límits

Una REST API amb RemObjects SDK rara vegada es juga en el servei «Hello World», sinó en els punts on funcionament, sistemes legats i integració xoquen: versionat sense aturades, comportament d’errors consistent en tots els punts finals, depuració reproductible en cadenes de proxy i la capacitat d’identificar i correlacionar inequívocament les peticions en cas de problema.

RemObjects SDK aporta per això molta infraestructura: serveis, formats de missatge, serialització, allotjament (p. ex. com a Windows- i Linux-Services o darrere d’IIS/Reverse Proxy) i punts definits per tractar errors de forma centralitzada. Allò que sovint manca en entorns de software empresarial consolidats és, però, un contracte aplicat de manera coherent: quins camps JSON són estables? Com senyalitzem errors? Com reconeixem una petició quan ha passat per load balancers, TLS-termination i diverses capes de backend?

L’enfocament següent (inclòs Delphi-snipet) mostra una línia robusta per a RemObjects SDK: versionar els contractes JSON, forçar la Correlation-ID (Request-ID per al seguiment), traduir Exceptions a codi d’estat HTTP i a objectes d’error JSON i, al mateix temps, no enfrontar la depuració amb l’operació. A més, abordem casos límit que apareixen amb regularitat en entorns reals: threading al servidor, accessos a bases de dades amb BDE-ablament amb enllaç natiu, capçaleres de proxy, timeouts i payloads de client „bruts“.

Decisió d’arquitectura: versionat pel tipus de medi en lloc de per l’URL

Moltes APIs versionen a través de rutes com /v1/. Això és pragmàtic, però en integracions de llarga durada (p. ex. connexions ERP/DMS/CRM) sovint deriva en duplicació d’URL, rutes dobles, proves duplicades i la pregunta «Quina versió fem servir, en realitat?» en els manuals d’explotació.

Una alternativa és versionar mitjançant el Media Type (negociació de contingut). El client envia, per exemple, Accept: application/vnd.company.order+json;v=2. El servidor llegeix la versió de manera determinista i adapta el comportament de contracte/DTO. Això funciona en cadenes de proxy i de cache si els headers es transmeten correctament. Per als administradors també és fàcil de verificar: una petició es pot reproduir amb Curl/Postman sense que canviïn les URLs.

RemObjects SDK no és «REST-purista», sinó un framework de serveis pragmàtic. Precisament per això val la pena la variant basada en tipus de medi: podeu conservar endpoints estables i alhora evolucionar els contractes. És important que la versió la avalueu sempre, que es prengui la decisió de manera centralitzada i que el resultat s’incorpori al context del vostre servei.

Quan falla la variant basada en l’header Accept?

A la pràctica hi ha tres punts de fallada típics que cal abordar prèviament:

  • Polítiques de proxy: Algunes regles de Reverse Proxy/WAF normalitzen o filtren l’Accept-Header. Aleshores la vostra API cau silenciosament al valor per defecte. Solució: revisar explícitament les regles del proxy, i si cal recórrer a X-Api-Version.
  • Llibreries client: Algunes llibreries HTTP de client estableixen el seu propi Accept-Header i sobreescriuen els valors. Solució: admetre la versió del contracte també com a paràmetre de consulta opcional (només com a fallback), o analitzar tolerantment l’Accept-Header al costat del servidor.
  • Caching: Si hi entra en joc el response-caching, la memòria cau ha de variar segons Accept (Vary: Accept), si no servirà la versió 1 a clients de la versió 2. Solució: establir conscientment Vary, o desactivar el caching a nivell d’API.

Fragment de codi: Request-Context, Correlation-ID, versió i mapeig d’errors

El codi està intencionadament dissenyat per integrar-se en projectes de servidor RemObjects existents: una petita capa de context, un parser per a la versió de l’API (provinent de Accept), un mecanisme de Correlation-ID i un mapeig central d’excepcions. Termes:

  • Correlation-ID: ID única per petició, que torna a aparèixer a la resposta i es referencia als logs.
  • Exception-Mapping: Traducció d’excepcions internes Delphi a objectes d’error estables i processables pel client (incl. HTTP-Status).
  • Contract-Version: Versió del contracte JSON que controla el comportament i els camps.
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;
// S'espera p. ex.: 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;
// En producció no mostrar detalls interns, ni SQL, ni rutes.
// Per a Debug/Stage es pot ampliar mitjançant configuració.
begin
  if E is EApiError then
    Exit(E.Message);

  if E is EArgumentException then
    Exit('Paràmetres invàlids.');

  Exit('Error intern.');
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.

Objectiu: context de la sol·licitud estable en lloc de «en algun lloc del Threadlocal»

El fragment separa deliberadament: TApiContext és l’estat mínim que voleu passar. A RemObjects SDK molt funciona a través del context servidor/-channel. En projectes heterogenis (p. ex. fils worker addicionals, cua de BD, tasques en segon pla) passar el context de forma explícita sovint és més robust que els Threadlocals implícits, perquè així feu més visibles la concurrència i els canvis de context.

Condicions marc: La variant dels Accept-Header pressuposa que el vostre reverse proxy (nginx, IIS ARR, Traefik) reenvia el header sense modificar-lo. En alguns entorns, els Accept-Header «poc comuns» són filtrats o agregats.

Trampes: El versionament via Accept val tant com els vostres tests. Si els clients utilitzen llibreries que sobrescriuen Accept, una API pot caure sobtadament al valor per defecte. Per a clients legacy és raonable tenir un fallback per defecte, però ha de ser visible al monitoring (p. ex. advertència de log «Version defaulted»).

Variants: Si preferiu versionar via X-Api-Version: el parser és idèntic, només que la font és un altre header. Des del punt de vista dels gateways això a vegades és més fàcil de controlar.

Integració en RemObjects SDK: Correlation-ID i mapeig d’excepcions a l’entrada del servei

L’efecte real s’aconsegueix quan apliqueu la mecànica de manera conseqüent al límit del vostre servidor: una vegada llegir des dels headers a l’entrada de la request, una altra traduir les excepcions a una resposta estable a la sortida. Segons el hosting (p. ex. RO-HTTP-Server, IIS-Hosting, Windows-/Windows- und Linux-Services) els punts d’enganxament concrets varien; el principi és el mateix: construir el context, invocar la lògica de negoci, mapar centralment les excepcions.

En projectes RemObjects sovint es treballa directament per mètode de servei. Això escala bé al principi, però falla en producció: cada mètode implementa el logging i el tractament d’errors de manera diferent. Una separació neta és una base de servei o un dispatcher que estandarditzi.

Procés pràctic (intencionadament breu i proper a la implementació)

  1. Llegir la Correlation-ID des del Request-Header X-Correlation-ID; si falta, generar-la al costat del servidor (p. ex. GUID).
  2. Llegir la versió del contracte des de Accept (o des de X-Api-Version).
  3. Registrar l’inici de la request: mètode, ruta, Correlation-ID, IP remota, iniciar mesura de durada.
  4. Executar la lògica de negoci; encapsular els accessos a BD tan com sigui possible en una transacció.
  5. Capturar excepcions: determinar l’estatus HTTP, generar un objecte d’error JSON, establir el Response-Header X-Correlation-ID.
  6. Registrar el final de la request: estatus, durada, si escau codi d’error.

Threading al servidor: Per què la Correlation-ID sense disciplina de context esdevé inútil

Un cas límit freqüent de Delphi: la mètode de servei desencadena treball asíncron (p. ex. generació d’informes, import, push a un DMS). Llavors el thread original de la request ja no és el que escriurà les línies de log més tard. Si la Correlation-ID només es coneix «al principi», la traçabilitat es trenca.

Regla pragmàtica: Tot allò que no es mantingui estrictament en el thread de la request ha de rebre el context de forma explícita. Encara que sembli més llistes de paràmetres, val la pena. Alternativament es pot treballar amb un objecte de context clarament definit, que s’entrega deliberadament als workers (en lloc de variables globals o singletons ocults).

Punts típics de canvi en servidors RemObjects/Delphi:

  • Connexions DB per fil: BDE-Ablosung mit nativer Anbindung-conexions no són automàticament segures per a múltiples fils. Un pool de connexions o una connexió per fil sovint és més adient que «una connexió global».
  • Límits de transacció: Si dins d’una petició hi ha diversos passos que pertanyen a la mateixa unitat lògica, la transacció ha de romandre dins d’aquesta unitat lògica. El treball asíncron no ha de continuar «per accident» dins de la mateixa transacció.
  • Cancel·lació: Si el client aborta (timeout del proxy, tancament del navegador), el servidor sovint continua executant. Valori de manera conscient si el treball en segon pla encara té sentit.

Accés a dades i codis d’error: 409 no és „també un 500”

En projectes d’integració, un mapeig d’errors net és més que cosmètica. Determina si l’altra part (ERP-Connector, ETL-job, portal de clients) pot reaccionar correctament. Algunes línies guia pràctiques provades en entorns Delphi/RemObjects:

  • 400 Bad Request: Validació, paràmetres faltants/invàlids, JSON no parsable. Important: La resposta ha de ser estable encara que el body estigui malmès.
  • 401/403: Separeu autenticació i autorització. 401 vol dir «identitat no existent/invàlida», 403 «identitat ok, però prohibit».
  • 404: La resource no existeix. Precaució pel que fa a la seguretat: no sempre convé revelar si alguna cosa existeix.
  • 409 Conflict: Conflicte de domini (p. ex. conflicte de versions, «l’estat no permet aquesta acció», violació de clau única quan té rellevància de negoci).
  • 422 Unprocessable Content: Si sintàcticament tot està bé però la validació de negoci falla (no tots els equips fan servir 422, però sovint és més clar que 400).
  • 500: Tot allò que no pugueu classificar netament. Això inclou també «DB down», «Timeout», «Unhandled Exception».

Truc específic de Delphi: Molts errors de BD apareixen com a excepcions genèriques. Val la pena, a la capa d’accés a dades, detectar situacions conegudes i mapar-les explícitament a EApiError. Important: no traspassar fragments SQL ni noms interns de taules/columnes al missatge cap al client. Aquests detalls van al log, no a la resposta.

Truc de depuració: errors reproducibles amb un «Contract Snapshot»

Pot ser inusual, però extremadament útil en producció: desar en errors (o de manera selectiva per a determinades IDs de correlació) un «snapshot» de les capçaleres de la petició + cos de la petició en un fitxer spool de depuració. No és un registre permanent (protecció de dades/volum), sinó una eina controlada per poder reproduir casos difícils des de prop de producció.

Important: un snapshot mai no ha de persistir sense filtrar els Authorization-Header, tokens o dades personals. A la pràctica això vol dir: redacció/mascarament i activació només per mitjà d’un feature-flag o d’una whitelist (p. ex. només per a certes IDs de correlació, períodes curts).

Implementació neta a la pràctica: mascarament en lloc d’omissió

En integracions reals, els camps «crítics» sovint són precisament els que necessiteu per depurar (p. ex. identificadors). En lloc d’ometre’ls de manera general, és preferible mascarar-los: substituir parcialment tokens, conservar només el domini dels correus electrònics, IBAN amb només les darreres xifres. Així el cas continua sent reproducible sense dispersar dades innecessàries pel sistema de fitxers. A més, el snapshot hauria d’estar clarament marcat com a artefacte de depuració i tenir un període d’emmagatzematge definit.

Seguretat i operació: retransmissió d’encapçalats, cadenes de proxy i Timeouts

Una REST API rarament acaba directament al client. És habitual trobar cadenes de reverse proxy, TLS-termination, WAF o API-Gateway. D’això se’n deriven punts pràctics:

  • IP remota: No us refieu a cegues de X-Forwarded-For. Acceptar-lo només de proxies de confiança i, en cas contrari, utilitzar la IP directa del socket. Als manuals d’operació hauria d’indicar-se quins hops són de confiança.
  • Timeouts: Si el proxy té 30 segons, però el vostre backend en necessita 2 minuts, generareu peticions fantasma. Establiu timeouts de forma coherent al llarg de la cadena i decidiu: petició sincrona o patró de treball (202 Accepted + endpoint d’estat).
  • Correlation-ID: Fixeu la Correlation-ID als headers de resposta perquè els administradors la puguin correlacionar entre els logs i el costat client. Si un gateway utilitza les seves pròpies Request-IDs: registreu i mapeu ambdues IDs.
  • Textos d’error: En producció, cap detall intern. Detalls de depuració només de manera controlada (Stage/Feature-Flag) i, en cas de dubte, només al log.

Context: Per què RemObjects SDK pot ser un avantatge aquí

En els ecosistemes Delphi els REST-Server sovint es construeixen amb frameworks més lleugers (p. ex. routers HTTP minimalistes). RemObjects SDK mostra la seva fortalesa quan ja teniu, o necessiteu, una arquitectura multinivell:

  • Límits clars de servei: els mètodes de servei són explícits i els contractes són versionables.
  • Transports i serialització: podeu utilitzar JSON, però també altres formats de missatge (segons la configuració), sense barrejar la lògica de negoci.
  • Operació: les opcions d’hosting i la integració amb els serveis Windows i Linux existents són planificables, incloent desplegaments nets.

L’enfocament mostrat complementa això amb els elements que sovint falten en el dia a dia: objectes d’error uniformes, versionatgе determinista i logging correlacionable. Especialment en software empresarial a mida amb cicles de vida llargs, això us estalvia temps en actualitzacions i en la integració de sistemes externs.

Conclusió: Val la pena l’esforç — i on falla l’enfocament?

El valor afegit apareix quan la vostra interfície REST no només «funciona», sinó que és operable de manera sostenible: contractes JSON estables, versionat sense proliferació d’URLs, errors traçables i depuració sense endevinalles. Precisament aquí l’enfocament amb Context, Correlation-ID i un mapeig central d’excepcions a RemObjects SDK és potent.

Límits d’ús: Si només teniu un punt final únic i efímer sense socis d’integració, la Media-Type-Versionierung pot semblar ràpidament sobreenginyeria. El Snapshot-Logging també té sentit només si implementeu el mascarament i l’activació de forma disciplinada. I: si la vostra pila de proxy optimitza o elimina headers, primer cal alinear la infraestructura; si no, estareu depurant la capa equivocada.

Si cal modernitzar una infraestructura de servidors Delphi existent o integrar netament una solució de software pròxima al procés en ERP/DMS/CRM, aquests mecanismes sovint marquen la diferència entre «funciona a les proves» i «funciona en producció».

En l’entorn funcional també tenen un paper important Delphi REST-API i REST-Server i Remobjects Sdk Delphi quan les integracions, els fluxos de dades i l’evolució han de funcionar de manera coordinada i fiable.

Parlar sobre un projecte o una iniciativa de modernització amb Net-Base.

Pas següent

Quan un tema esdevé un projecte real, l'arquitectura, l'entorn existent i les operacions s'haurien de considerar conjuntament des de bon començament.

No només donem suport en qüestions puntuals, sinó també quan, a partir de fragments de codi font, temes de sistemes heredats o idees de portal, ha de sorgir un projecte empresarial sòlid.

  • L'estat actual, la visió objectiu i els riscos tècnics s'avaluen conjuntament.
  • REST, l'accés a les dades, els portals i el desplegament no es releguen a fases posteriors.
  • Vostè veurà aviat quin camí és econòmicament i operativament viable.

Comparteix la publicació

Comparteix aquesta publicació directament

LinkedIn, X, XING, Facebook, WhatsApp i E-Mail estan disponibles de forma immediata. Per a Instagram preparem directament l’enllaç i un text breu.

Correu electrònic

Instagram s'obre en una pestanya nova. L'enllaç i el text curt es copien prèviament al porta-retalls.