Net-Base Tímarit

09.06.2026

REST API með RemObjects SDK: hreint útgáfustjórnun og bilanaleit á JSON-endapunktum (Delphi kóðasneið)

Wie Sie mit RemObjects SDK in Delphi eine REST API aufbauen, die im Betrieb nicht zerbricht: stabile JSON-Verträge, Versionierung ohne URL-Wildwuchs, Correlation-ID durch alle Schichten, zentrales Error-Mapping, Snapshot-Logging für harte Debug-Fälle sowie praxisnahe Hinweise...

09.06.2026

Frá tímaritsþema til verkefnaframkvæmdar

Viðeigandi þjónustu- og tæknisíður fyrir greinina

Warum „REST API mit RemObjects SDK“ in der Praxis oft an den Rändern entscheidet

Eine REST API mit RemObjects SDK steht und fällt selten am „Hello World“-Service, sondern an den Stellen, an denen Betrieb, Legacy und Integration aufeinanderprallen: Versionierung ohne Stillstand, konsistentes Fehlerverhalten über alle Endpunkte, reproduzierbares Debugging bei Proxy-Ketten und die Fähigkeit, Requests im Problemfall eindeutig zu korrelieren.

RemObjects SDK bringt dafür viel Infrastruktur mit: Services, Message-Formate, Serialisierung, Hosting (z. B. als Windows- und Linux-Services oder hinter IIS/Reverse Proxy) und definierte Stellen, um Fehler zentral zu behandeln. Was in gewachsenen Business-Software-Landschaften aber häufig fehlt, ist ein konsequent durchgezogener Vertrag: Welche JSON-Felder sind stabil? Wie signalisieren wir Fehler? Wie erkennen wir einen Request wieder, wenn er durch Load Balancer, TLS-Termination und mehrere Backend-Schichten gelaufen ist?

Der folgende Ansatz (inklusive Delphi-Snipsel) zeigt eine robuste Linie für RemObjects SDK: JSON-Verträge versionieren, Correlation-ID (Request-ID zur Nachverfolgung) erzwingen, Exceptions in HTTP-Status und JSON-Fehlerobjekte übersetzen und dabei Debugging und Betrieb nicht gegeneinander ausspielen. Zusätzlich schauen wir auf Randfälle, die in echten Umgebungen regelmäßig auftreten: Threading im Server, Datenbank-Zugriffe mit BDE-Ablosung mit nativer Anbindung, Proxy-Header, Timeouts und „schmutzige“ Client-Payloads.

Architektur-Entscheidung: Versionierung über Medien-Typ statt URL

Viele APIs versionieren über Pfade wie /v1/. Das ist pragmatisch, aber in länger laufenden Integrationen (z. B. ERP/DMS/CRM-Anbindungen) führt es oft zu URL-Duplizierung, doppelten Routen, doppelten Tests und „Welche Version nutzen wir eigentlich?“ in Betriebshandbüchern.

Eine Alternative ist Versionierung über den Media Type (Content Negotiation). Der Client sendet z. B. Accept: application/vnd.company.order+json;v=2. Der Server liest die Version deterministisch aus und passt Contract/DTO-Verhalten an. Das funktioniert in Proxy- und Cache-Ketten, wenn die Header sauber weitergereicht werden. Für Admins ist es zudem gut prüfbar: Ein Request lässt sich per Curl/Postman reproduzieren, ohne dass sich URLs unterscheiden.

RemObjects SDK ist nicht „REST-puristisch“, sondern ein pragmatisches Service-Framework. Genau deshalb lohnt sich die Medien-Typ-Variante: Sie können stabile Endpunkte behalten und dennoch Verträge weiterentwickeln. Wichtig ist, dass Sie die Version immer auswerten, an einer Stelle zentral entscheiden und das Ergebnis in Ihren Service-Kontext übernehmen.

Wann kippt die Accept-Header-Variante?

In der Praxis gibt es drei typische Bruchstellen, die man vorab adressieren sollte:

  • Proxy-Policies: Manche Reverse Proxies/WAF-Regeln normalisieren oder filtern Accept-Header. Dann fällt Ihre API still auf Default zurück. Lösung: Proxy-Regeln explizit prüfen, ggf. auf X-Api-Version ausweichen.
  • Client-Libraries: Einige HTTP-Clients setzen eigene Accept-Header und überschreiben Werte. Lösung: Contract-Version auch als optionalen Query-Parameter unterstützen (nur als Fallback), oder den Accept-Header serverseitig tolerant parsen.
  • Caching: Wenn Response-Caching im Spiel ist, muss der Cache nach Accept variieren (Vary: Accept), sonst liefert er Version 1 an Version-2-Clients. Lösung: Vary bewusst setzen, oder Caching auf API-Ebene deaktivieren.

Source-Schnipsel: Request-Context, Correlation-ID, Version und Error-Mapping

Der Code ist bewusst so geschnitten, dass er sich in bestehende RemObjects-Serverprojekte integrieren lässt: eine kleine Context-Schicht, ein Parser für die API-Version (aus Accept), ein Correlation-ID-Mechanismus und ein zentrales Exception-Mapping. Begriffe:

  • Correlation-ID: Eindeutige ID pro Request, die im Response wieder auftaucht und in Logs referenziert wird.
  • Exception-Mapping: Übersetzung interner Delphi-Exceptions in stabile, client-verarbeitbare Fehlerobjekte (inkl. HTTP-Status).
  • Contract-Version: Version des JSON-Vertrags, die Verhalten und Felder steuert.
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.

Zweck: Stabiler Request-Kontext statt „irgendwo im Threadlocal“

Der Snipsel trennt bewusst: TApiContext ist der minimale Zustand, den Sie durchreichen wollen. In RemObjects SDK läuft viel über Server-/Channel-Kontext. In heterogenen Projekten (z. B. zusätzliche Worker-Threads, DB-Queue, Hintergrundjobs) ist explizites Durchreichen aber oft robuster als implizite Threadlocals, weil Sie damit Nebenläufigkeit und Kontextwechsel sichtbarer machen.

Randbedingungen: Die Accept-Header-Variante setzt voraus, dass Ihr Reverse Proxy (nginx, IIS ARR, Traefik) den Header unverändert weiterreicht. In manchen Umgebungen werden „ungewöhnliche“ Accept-Header gefiltert oder zusammengefasst.

Stolperfallen: Versionierung über Accept ist nur so gut wie Ihre Tests. Wenn Clients Libraries nutzen, die Accept überschreiben, kann eine API plötzlich auf Default zurückfallen. Für Legacy-Clients ist ein Default-Fallback sinnvoll, aber er muss in Monitoring sichtbar sein (z. B. Log-Warnung „Version defaulted“).

Varianten: Wenn Sie Versionierung lieber über X-Api-Version machen: Der Parser ist identisch, nur die Quelle ist ein anderer Header. Aus Sicht von Gateways ist das manchmal einfacher zu kontrollieren.

Integration in RemObjects SDK: Correlation-ID und Exception-Mapping am Service-Einstieg

Die eigentliche Wirkung entsteht, wenn Sie die Mechanik konsequent am Rand Ihres Servers anwenden: einmal am Request-Einstieg aus Headern lesen, einmal am Exception-Ausgang in eine stabile Response übersetzen. Je nach Hosting (z. B. RO-HTTP-Server, IIS-Hosting, selbst betriebener Windows-/Windows- und Linux-Services) unterscheiden sich die konkreten Hook-Punkte; das Prinzip bleibt gleich: Context bauen, Business-Logik aufrufen, Exceptions zentral mappen.

In RemObjects-Projekten wird häufig pro Service-Methode direkt gearbeitet. Das skaliert anfangs gut, kippt aber bei Betrieb: Jede Methode baut Logging und Fehlerbehandlung anders. Ein sauberer Schnitt ist eine Service-Basis oder ein Dispatcher, der standardisiert.

Praktischer Ablauf (bewusst kurz und implementierungsnah)

  1. Correlation-ID aus Request-Header X-Correlation-ID lesen; wenn fehlt, serverseitig erzeugen (z. B. GUID).
  2. Contract-Version aus Accept lesen (oder aus X-Api-Version).
  3. Request-Start loggen: Methode, Pfad, Correlation-ID, Remote IP, Dauer-Messung starten.
  4. Business-Logik ausführen; DB-Zugriffe möglichst transaktional kapseln.
  5. Exception abfangen: HTTP-Status bestimmen, JSON-Fehlerobjekt erzeugen, Response-Header X-Correlation-ID setzen.
  6. Request-Ende loggen: Status, Dauer, ggf. Fehlercode.

Threading im Server: Warum Correlation-ID ohne Kontext-Disziplin wertlos wird

Ein häufiger Delphi-Randfall: Die Service-Methode triggert asynchrone Arbeit (z. B. Report-Generierung, Import, Push in ein DMS). Dann ist der ursprüngliche Request-Thread nicht mehr der, der später Logzeilen schreibt. Wenn die Correlation-ID nur „am Anfang“ bekannt ist, zerfällt die Nachverfolgbarkeit.

Pragmatische Regel: Alles, was nicht strikt im Request-Thread bleibt, bekommt den Context explizit übergeben. Auch wenn das nach mehr Parameterlisten aussieht, zahlt es sich aus. Alternativ kann man mit einem klar definierten Kontext-Objekt arbeiten, das bewusst an Worker übergeben wird (statt globaler Variablen oder versteckter Singletons).

Typische Kipppunkte in RemObjects-/Delphi-Servern:

  • DB-Connections pro Thread: BDE-Ablosung mit nativer Anbindung-Verbindungen sind nicht automatisch thread-sicher teilbar. Ein Connection-Pool oder pro Thread eine Verbindung ist häufig sinnvoller als „eine globale Connection“.
  • Transaktionsgrenzen: Wenn Sie innerhalb eines Requests mehrere Schritte haben, die zusammengehören, muss die Transaktion in der gleichen logischen Einheit bleiben. Asynchrone Arbeit darf nicht „aus Versehen“ in der gleichen Transaktion weiterlaufen.
  • Cancellation: Wenn der Client abbricht (Proxy timeout, Browser closed), läuft der Server oft weiter. Überlegen Sie bewusst, ob Hintergrundarbeit dann noch Sinn ergibt.

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

In Integrationsprojekten ist sauberes Error-Mapping mehr als Kosmetik. Es entscheidet, ob ein Gegenüber (ERP-Connector, ETL-Job, Kundenportal) korrekt reagieren kann. Ein paar praxisnahe Leitplanken, die sich in Delphi/RemObjects-Umgebungen bewährt haben:

  • 400 Bad Request: Validierung, fehlende/ungültige Parameter, JSON nicht parsebar. Wichtig: Die Antwort soll stabil bleiben, auch wenn der Body kaputt ist.
  • 401/403: Authentifizierung vs. Berechtigung trennen. 401 bedeutet „keine/ungültige Identität“, 403 „Identität ok, aber verboten“.
  • 404: Ressource existiert nicht. Vorsicht bei Security: Nicht immer verraten, ob etwas existiert.
  • 409 Conflict: Fachlicher Konflikt (z. B. Versionskonflikt, „Status erlaubt diese Aktion nicht“, eindeutige Schlüsselverletzung, wenn sie fachlich relevant ist).
  • 422 Unprocessable Content: Wenn syntaktisch alles ok ist, aber fachliche Validierung scheitert (nicht jedes Team nutzt 422, aber es ist oft klarer als 400).
  • 500: Alles, was Sie nicht sauber klassifizieren können. Dazu gehört auch „DB down“, „Timeout“, „Unhandled Exception“.

Delphi-spezifischer Kniff: Viele DB-Fehler kommen als generische Exceptions hoch. Es lohnt sich, an der Datenzugriffsschicht gezielt auf bekannte Situationen zu prüfen und sie in EApiError zu überführen. Wichtig dabei: Keine SQL-Fragmente oder internen Tabellen-/Spaltennamen in die Client-Message übernehmen. Diese Details gehören ins Log, nicht in die Response.

Debugging-Kniff: reproduzierbare Fehler durch „Contract Snapshot“

Ungewöhnlich, aber im Betrieb extrem hilfreich: Speichern Sie bei Fehlern (oder gezielt bei bestimmten Correlation-IDs) einen „Snapshot“ aus Request-Headern + Request-Body in einer Debug-Spool-Datei. Das ist kein Dauerlogging (Datenschutz/Volumen), sondern ein kontrolliertes Werkzeug, um schwer reproduzierbare Fälle aus Produktionsnähe nachzustellen.

Wichtig: Ein Snapshot darf niemals ungefiltert Auth-Header, Tokens oder personenbezogene Daten persistieren. In der Praxis bedeutet das: Redaction (Maskierung) und Aktivierung nur über Feature-Flag oder Whitelist (z. B. nur für bestimmte Correlation-IDs, kurze Zeitfenster).

Saubere Umsetzung in der Praxis: Maskieren statt Weglassen

In echten Integrationen sind gerade die „kritischen“ Felder oft die, die man zum Debuggen bräuchte (z. B. Identifikatoren). Statt pauschalem Weglassen ist Maskieren besser: Token teilweise ersetzen, E-Mail nur Domain behalten, IBAN nur die letzten Ziffern. So bleibt der Fall reproduzierbar, ohne unnötige Daten im Dateisystem zu verteilen. Zusätzlich sollte der Snapshot klar als Debug-Artefakt gekennzeichnet sein und eine definierte Aufbewahrungszeit haben.

Sicherheit und Betrieb: Header-Weitergabe, Proxy-Ketten und 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: Verlassen Sie sich nicht blind auf X-Forwarded-For. Nur aus vertrauenswürdigen Proxies übernehmen und sonst die direkte Socket-IP nutzen. In Betriebshandbüchern sollte stehen, welche Hops „trusted“ sind.
  • Timeouts: Wenn Proxy 30 Sekunden hat, Ihr Backend aber 2 Minuten braucht, erzeugen Sie Ghost-Requests. Legen Sie Timeouts entlang der Kette konsistent fest und entscheiden Sie: synchroner Request oder Job-Pattern (202 Accepted + Status-Endpunkt).
  • Correlation-ID: Setzen Sie die Correlation-ID in Response-Headern, damit Admins sie aus Logs und Client-Seite zusammenführen können. Wenn ein Gateway eigene Request-IDs nutzt: beide IDs loggen und abbilden.
  • Fehlertexte: Im Produktivbetrieb keine internen Details. Debug-Details nur kontrolliert (Stage/Feature-Flag) und im Zweifel nur im 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: Service-Methoden sind explizit, Contracts sind versionierbar.
  • Transporte und Serialisierung: Sie können JSON sprechen, aber auch andere Message-Formate (je nach Setup), ohne die Fachlogik zu verquirlen.
  • Betrieb: Hosting-Optionen und Integration in bestehende Windows- und Linux-Services sind planbar, inklusive sauberer Rollouts.

Der gezeigte Ansatz ergänzt das um die Teile, die im Alltag oft fehlen: einheitliche Fehlerobjekte, deterministische Versionierung und korrelierbares Logging. Gerade bei individueller Unternehmenssoftware mit langen Lebenszyklen sparen Sie damit Zeit bei Updates und bei der Integration externer Systeme.

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

Der Mehrwert entsteht, wenn Ihre REST-Schnittstelle nicht nur „funktioniert“, sondern dauerhaft betreibbar ist: stabile JSON-Verträge, Versionierung ohne URL-Wildwuchs, nachvollziehbare Fehler und Debugging ohne Ratespiel. Genau dort ist der Ansatz mit Context, Correlation-ID und zentralem Exception-Mapping in RemObjects SDK stark.

Einsatzgrenzen: Wenn Sie nur einen einzelnen, kurzlebigen Endpunkt ohne Integrationspartner haben, wirkt Media-Type-Versionierung schnell wie Overengineering. Auch Snapshot-Logging ist nur sinnvoll, wenn Sie Redaction und Aktivierung diszipliniert implementieren. Und: Wenn Ihr Proxy-Stack Header „optimiert“ oder entfernt, müssen Sie zuerst die Infrastruktur geradeziehen, sonst debuggen Sie die falsche Schicht.

Wenn Sie eine bestehende Delphi-Serverlandschaft modernisieren oder eine prozessnahe Softwarelösung sauber in ERP/DMS/CRM integrieren müssen, sind genau diese Mechanismen aber häufig der Unterschied zwischen „läuft im Test“ und „läuft im Betrieb“.

Im fachlichen Umfeld spielen auch Delphi REST-API und REST-Server und Remobjects Sdk Delphi eine wichtige Rolle, wenn Integrationen, Datenflüsse und Weiterentwicklung sauber zusammenspielen müssen.

Projekt oder Modernisierungsvorhaben mit Net-Base besprechen.

Næsta skref

Þegar úr málinu verður raunverulegt verkefni ber að skoða arkitektúr, núverandi kerfi og rekstur snemma saman.

Við styðjum ekki aðeins við einstakar spurningar, heldur einnig þegar úr kóðabútum, eldri kerfum eða gáttahugmyndum þarf að verða traust fyrirtækjaverkefni.

  • Núverandi staða, markmynd og tæknileg áhætta eru metin saman.
  • REST, gagnaaðgangur, gáttir og innleiðing eru ekki skildir eftir til síðar.
  • Það sést snemma hvaða leið er fjárhagslega og rekstrarlega sjálfbær.

Deila færslu

Deila þessari færslu beint

LinkedIn, X, XING, Facebook, WhatsApp og tölvupóstur eru strax tiltækir. Fyrir Instagram undirbúum við hlekk og stuttan texta beint.

Tölvupóstur

Instagram opnast í nýjum flipa. Tengill og stuttur texti eru afritaðir í klippiborðið á undan.