Dergi konusundan proje pratiğine
İçeriğe Uygun Hizmet ve Teknik Sayfalar
Neden „REST API mit RemObjects SDK“ gerçek kullanımda sıklıkla uç durumlarda belirleyici olur
Bir REST API mit RemObjects SDK nadiren “Hello World”-servisine bağlıdır; esas belirleyici olan, işletme, legacy ve entegrasyonun çarpıştığı durumlardır: kesintisiz sürümleme, tüm uç noktalarda tutarlı hata davranışı, proxy zincirlerinde tekrarlanabilir hata ayıklama ve sorun durumunda istekleri kesin şekilde ilişkilendirebilme yeteneği.
RemObjects SDK bunun için geniş bir altyapı sunar: Services, Message-Formate, Serialisierung, Hosting (z. B. als Windows- und Linux-Services oder hinter IIS/Reverse Proxy) ve hataları merkezi olarak ele almak için tanımlı noktalar. Ancak olgunlaşmış kurumsal yazılım ortamlarında sıkça eksik olan şey, tutarlı biçimde uygulanmış bir sözleşmedir: Hangi JSON alanları stabildir? Hataları nasıl sinyalleriz? Bir istek, Load Balancer, TLS-Termination ve birden fazla arka uç katmanından geçmişse onu nasıl yeniden tanırız?
Aşağıdaki yaklaşım (inklusive Delphi-Snipsel) RemObjects SDK için sağlam bir çizgi gösterir: JSON sözleşmelerini sürümlemek, Correlation-ID (izleme için Request-ID) zorunlu kılmak, istisnaları HTTP durum kodlarına ve JSON hata objelerine çevirmek ve bu esnada hata ayıklama ile işletmeyi birbirine karşı kullanmamak. Ayrıca gerçek ortamlarda düzenli olarak ortaya çıkan kenar durumlarına bakıyoruz: Sunucuda thread yönetimi, BDE-Ablosung ile yerel veritabanı erişimleri, proxy headerları, zaman aşımı ve “kirli” istemci payloadları.
Mimari karar: Sürümleme URL yerine Medya-Tipi üzerinden
Birçok API /v1/ gibi yollar üzerinden sürümlenir. Bu pragmatik bir yaklaşımdır, fakat uzun süreli entegrasyonlarda (ör. ERP/DMS/CRM bağlantıları) genellikle URL çoğaltılmasına, çift rotalara, çift testlere ve işletme el kitaplarında “hangi sürümü kullanıyoruz?” sorusuna yol açar.
Alternatif olarak sürümlemeyi Media Type (Content Negotiation) üzerinden yapmak mümkündür. Örnek: İstemci Accept: application/vnd.company.order+json;v=2 gönderir. Sunucu sürümü deterministik olarak header’dan okur ve Contract/DTO davranışını ayarlar. Bu, headerlar düzgün iletildiği sürece proxy ve cache zincirlerinde çalışır. Yöneticiler için ayrıca doğrulanması kolaydır: Bir isteği Curl/Postman ile URL’ler değişmeden yeniden üretebilirsiniz.
RemObjects SDK, „REST-puristisch“ değildir, aksine pragmatik bir servis çerçevesidir. Tam da bu yüzden medya-tipi varyantı fayda sağlar: Stabil uç noktaları korurken sözleşmeleri geliştirebilirsiniz. Önemli olan sürümü her zaman değerlendirmek, merkezi bir noktada karar vermek ve sonucu servis bağlamınıza aktarmaktır.
Accept-Header varyantı ne zaman başarısız olur?
Pratikte önceden ele alınması gereken üç tipik kopma noktası vardır:
- Proxy politikaları: Bazı Reverse Proxies/WAF kuralları Accept-header’ı normalleştirir veya filtreler. Bu durumda API’niz sessizce varsayılana döner. Çözüm: Proxy kurallarını açıkça kontrol edin, gerekirse
X-Api-Versione başvurun. - İstemci kütüphaneleri: Bazı HTTP-istemcileri kendi Accept-header’larını ayarlar ve değerleri üzerine yazar. Çözüm: Contract sürümünü opsiyonel bir sorgu parametresi olarak da destekleyin (sadece yedek olarak) veya Accept-header’ı sunucu tarafında toleranslı biçimde ayrıştırın.
- Önbellekleme: Response-Caching devredeyse, önbellek
Accept‚e göre varye olmalıdır (Vary: Accept), aksi halde sürüm 1’i sürüm-2 istemcilerine teslim eder. Çözüm:Vary‚yı bilinçli olarak ayarlamak veya API düzeyinde önbellelemeyi devre dışı bırakmak.
Kaynak Kesit: Request-Context, Correlation-ID, Sürüm ve Error-Mapping
Kod kasıtlı olarak mevcut RemObjects-Serverprojekte ile entegre edilebilecek şekilde düzenlenmiştir: küçük bir Context-Schicht, API sürümü için bir Parser (aus Accept), bir Correlation-ID mekanizması ve merkezi bir Exception-Mapping. Begriffe:
- Correlation-ID: Her istek için benzersiz bir ID; yanıtta tekrar görünür ve loglarda referanslanır.
- Exception-Mapping: Dahili Delphi-Exceptions’in kararlı, istemci tarafından işlenebilir hata nesnelerine çevrilmesi (HTTP durumu dahil).
- Contract-Version: JSON sözleşmesinin sürümü; davranışı ve alanları kontrol eder.
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.Amaç: “bir yerlerde Threadlocal” yerine kararlı bir Request bağlamı
Kod parçası kasıtlı olarak ayırır: TApiContext, iletmek istediğiniz minimum durumdur. RemObjects SDK’da pek çok şey sunucu-/kanal bağlamı üzerinden yürür. Heterojen projelerde (ör. ek worker thread’ler, DB kuyruğu, arka plan işleri) açıkça iletmek genellikle örtük threadlocal’lardan daha sağlamdır; çünkü eşzamanlılık ve bağlam değişimlerini daha görünür kılar.
Çerçeve koşulları: Accept-Header varyantı, Reverse Proxy’nizin (nginx, IIS ARR, Traefik) header’ı değişmeden ilettiğini varsayar. Bazı ortamlarda “alışılmadık” Accept header’lar filtrelenir veya birleştirilir.
Tuzaklar: Accept üzerinden yapılan versiyonlama, testleriniz kadar iyidir. Eğer istemciler, Accept’i üzerine yazan kütüphaneler kullanıyorsa, bir API aniden varsayılan sürüme dönebilir. Legacy istemciler için bir default-fallback mantıklı olabilir; ancak bunun monitoring içinde görünür olması gerekir (ör. log-uyarısı „Version defaulted“).
Varyantlar: Versiyonlamayı tercih ederek X-Api-Version üzerinden yapmak isterseniz: Parser aynı kalır, yalnızca kaynağı farklı bir header olur. Gateway’ler açısından bazen bu kontrol etmesi daha kolaydır.
RemObjects SDK’ye entegrasyon: Service girişinde Correlation-ID ve Exception-Mapping
Asıl etki, mekaniklere sunucunuzun kenarında tutarlı şekilde uyguladığınızda ortaya çıkar: bir kere isteğin girişinde header’lardan okuyun, bir kere exception çıkışında kararlı bir response’a çevirin. Hosting’e göre (ör. RO-HTTP-Server, IIS-hosting, kendi işlettiğiniz Windows-/Windows- ve Linux-Services) somut hook noktaları değişir; prensip aynıdır: Bağlam kurun, iş mantığını çağırın, exception’ları merkezi olarak map’leyin.
RemObjects projelerinde sıklıkla her servis metodunda doğrudan çalışılır. Bu başta iyi ölçeklenir, ancak işletme aşamasında bozulur: Her metod farklı şekilde logging ve hata işleme kurar. Temiz bir kesit, standartlaştırılmış bir Service-temeli veya bir Dispatcherdır.
Pratik Süreç (bilerek kısa ve uygulamaya yakın)
- İstek header’ından Correlation-ID
X-Correlation-IDokuyun; yoksa sunucu tarafında üretin (ör. GUID). - Contract-sürümünü
Accept‚ten okuyun (veyaX-Api-Versionüzerinden). - İstek başlangıcını loglayın: metod, yol, Correlation-ID, uzak IP; süre ölçümünü başlatın.
- İş mantığını çalıştırın; DB erişimlerini mümkünse işlem içinde kapsülleyin.
- Exception yakalayın: HTTP durumu belirleyin, JSON hata nesnesi oluşturun, Response header
X-Correlation-IDayarlayın. - İstek bitişini loglayın: durum, süre, gerekiyorsa hata kodu.
Sunucuda Threading: Bağlam disiplini olmadan Correlation-ID neden değersizleşir
Sık görülen bir Delphi kenar durumu: Servis metodu, asenkron iş tetikler (ör. rapor oluşturma, import, bir DMS’e push). Bu durumda orijinal request thread’i daha sonra log satırlarını yazan thread olmayabilir. Correlation-ID yalnızca “başlangıçta” biliniyorsa, izlenebilirlik dağılır.
Pragmatik kural: İstek-iş parçacığında kalmayan her şeye bağlamı açıkça geçirin. Daha uzun parametre listeleri gibi görünse bile buna değer. Alternatif olarak, worker’a kasıtlı olarak geçirilen net tanımlı bir bağlam nesnesi ile çalışın (global değişkenler veya gizli singleton’lar yerine).
RemObjects-/Delphi-sunucularında tipik kırılma noktaları:
- İş Parçacığı Başına DB-Verbindungen: BDE-Ablosung mit nativer Anbindung-Verbindungen otomatik olarak iş parçacıkları arasında thread-güvenli şekilde paylaşılamaz. Bir bağlantı havuzu veya her iş parçacığı için ayrı bir bağlantı, genellikle „tek bir global Connection“ yerine daha mantıklıdır.
- Transaktionsgrenzen: Bir Request içinde birlikte olması gereken birden fazla adım varsa, işlem aynı mantıksal birimde kalmalıdır. Eşzamansız işler kazara aynı işlem içinde devam etmemelidir.
- Cancellation: İstemci bağlantıyı sonlandırdığında (Proxy timeout, Browser closed) sunucu çoğu durumda çalışmaya devam eder. Arka plan işlemlerinin bu durumda hâlâ anlamlı olup olmadığını bilinçli şekilde değerlendirin.
Veri erişimi ve hata kodları: 409 ist nicht „auch ein 500“
Entegrasyon projelerinde temiz bir hata-eşlemesi kozmetik bir detaydan öteye gider. Karşı tarafın (ERP-Connector, ETL-Job, Müşteriportal) doğru tepki verip veremeyeceğini bu belirler. Delphi/RemObjects-ortamlarında işe yaradığı görülen birkaç pratik kural:
- 400 Bad Request: Doğrulama hatası, eksik/geçersiz parametreler, JSON ayrıştırılamıyor. Önemli: Gövde bozuk olsa bile yanıt kararlı kalmalıdır.
- 401/403: Kimlik doğrulama ile yetkilendirmeyi ayırın. 401 „kimlik yok/geçersiz“, 403 „kimlik tamam, ancak yetki yok“ anlamına gelir.
- 404: Kaynak mevcut değil. Güvenlik açısından dikkat: Bir nesnenin varlığını her zaman ifşa etmeyin.
- 409 Conflict: İşe özgü çatışma (ör. sürüm çatışması, „durum bu işlemi izin vermiyor“, benzersiz anahtar ihlali — iş açısından anlamlıysa).
- 422 Unprocessable Content: Sözdizimsel olarak her şey doğruysa ama iş kurallarına göre doğrulama başarısız oluyorsa (her ekip 422 kullanmıyor; yine de çoğu durumda 400’tan daha açıklayıcıdır).
- 500: Temiz şekilde sınıflandıramadığınız her şey. Buna veritabanı kapalı, zaman aşımı veya yakalanmamış istisnalar dahildir.
Delphi’e özgü bir uygulama notu: Birçok DB hatası genel exception olarak yükselir. Veri erişim katmanında bilinen durumları tespit edip bunları EApiError olarak dönüştürmek faydalıdır. Önemli: SQL parçalarını veya dahili tablo/sütun adlarını istemci mesajına dahil etmeyin. Bu ayrıntılar log’larda yer almalı, yanıtta olmamalıdır.
Debugging-Kniff: reproduzierbare Fehler durch „Contract Snapshot“
Alışılmadık ama işletmede son derece faydalı bir yöntem: Hatalarda (veya belirli Correlation-IDs için seçici olarak) Request-Headern + Request-Body içeren bir „snapshot“ı bir debug-spool-dosyasına kaydedin. Bu sürekli bir loglama değildir (veri koruma/hacim), üretime yakın ortamdan zor tekrar üretilebilir vakaları yeniden oluşturmak için kontrollü bir araçtır.
Önemli: Bir snapshot asla filtrelenmemiş Auth-Header’ları, token’ları veya kişisel verileri kalıcı olarak saklamamalıdır. Pratikte bu şu demektir: Redaction (maskleme) ve etkinleştirme yalnızca bir feature-flag veya beyaz liste üzerinden yapılmalıdır (ör. sadece belirli Correlation-ID’ler için, kısa zaman aralıkları).
Pratikte doğru uygulama: Maskieren statt Weglassen
Gerçek entegrasyonlarda tam da „kritik“ alanlar genellikle hata ayıklamak için ihtiyaç duyulan alanlardır (ör. tanımlayıcılar). Toplu olarak tamamen çıkarmak yerine maskeleme daha uygundur: token’ları kısmen değiştirin, e-posta için yalnızca alan adını saklayın, IBAN’ın sadece son rakamlarını tutun. Böylece vaka yeniden üretilebilir kalır, gereksiz veriler dosya sisteminde yayılmaz. Ek olarak, snapshot açıkça bir debug-artefaktı olarak işaretlenmeli ve tanımlı bir saklama süresine sahip olmalıdır.
Güvenlik ve İşletim: 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“.
Uzmanlık bağlamında, Delphi REST-API ve REST-Sunucu ile Remobjects Sdk Delphi de önemli bir rol oynar; entegrasyonlar, veri akışları ve ileriye dönük geliştirmelerin düzgün bir şekilde birlikte çalışması gerektiğinde.
Sonraki adım
Konu gerçek bir projeye dönüştüğünde, mimari, mevcut yapı ve işletme erken aşamada birlikte ele alınmalıdır.
Bireysel sorularda destek vermekle kalmıyoruz; kaynak kodu parçacıklarından, legacy konularından veya portal fikirlerinden sağlam bir kurumsal projeye dönüşene kadar da destek veriyoruz.
- Mevcut durum, hedef durum ve teknik riskler birlikte değerlendirilir.
- REST, veri erişimi, portallar ve Rollout sonraki işler olarak ertelenmez.
- Hangi yolun ekonomik ve işletme açısından uygulanabilir olduğunu erken görürsünüz.