Dergi konusundan proje pratiğine
İçeriğe Uygun Hizmet ve Teknik Sayfalar
Neden „REST API mit RemObjects SDK“ pratikte çoğunlukla sınır konularında belirleyici olur
REST API mit RemObjects SDK nadiren bir “Hello World” servisine dayanır; asıl belirleyici olan, işletme, legacy ve entegrasyonun çarpıştığı noktalardır: durmaksızın sürdürülmesi gereken versiyonlama, tüm uç noktalarda tutarlı hata davranışı, proxy zincirlerinde tekrarlanabilir hata ayıklama ve sorun durumunda istekleri kesin olarak ilişkilendirebilme yeteneği.
RemObjects SDK bu ihtiyaçlar için çok sayıda altyapı getirir: servisler, mesaj formatları, serileştirme, barındırma (ör. Windows- ve Linux-Services veya IIS/Reverse Proxy arkasında) ve hataların merkezi ele alınabileceği tanımlı noktalar. Ancak olgunlaşmış kurumsal yazılım ortamlarında genellikle eksik olan, tutarlı biçimde uygulanmış bir kontrattır: Hangi JSON alanları stabil kalacak? Hataları nasıl ileteceğiz? Bir istek, Load Balancer, TLS-terminasyonu ve birden fazla backend katmandan geçtikten sonra nasıl yeniden tanımlanır?
Aşağıdaki yaklaşım (ve Delphi snipselleri dahil) RemObjects SDK için sağlam bir çizgi gösterir: JSON kontratlarını versiyonlama, Correlation-ID (izleme için istek kimliği) zorunlu kılma, Exceptions’ları HTTP durum kodlarına ve JSON hata nesnelerine çevirme ve bütün bunları yaparken hata ayıklama ile işletmeyi birbirine karşı kullanmama. Ayrıca gerçek ortamlarda düzenli olarak ortaya çıkan sınır durumlarına bakıyoruz: sunucuda threading, veritabanı erişimleri için BDE-Ablösung ile yerel bağlantı, proxy header’ları, zaman aşımı durumları ve “kirli” istemci yükleri.
Mimari karar: URL yerine Medya Türü üzerinden versiyonlama
Birçok API yol üzerinden versiyonlar (/v1/ gibi). Bu pragmatik bir yaklaşımdır, ancak uzun süre devam eden entegrasyonlarda (ör. ERP/DMS/CRM bağlantıları) URL çoğalmasına, çift rota ve çift teste ve işletme el kitaplarında “aslında hangi versiyonu kullanıyoruz?” sorununa yol açar.
Alternatif, Medya Türü (Content Negotiation) üzerinden versiyonlamadır. İstemci örneğin Accept: application/vnd.company.order+json;v=2 gönderir. Sunucu versiyonu deterministik olarak okur ve kontrat/DTO davranışını buna göre uyarlar. Header’lar düzgün iletildiği sürece proxy ve cache zincirlerinde çalışır. Yönetim açısından da kontrol edilebilir: Bir isteği Curl/Postman ile URL değiştirmeye gerek kalmadan yeniden oluşturmak mümkündür.
RemObjects SDK, “REST-puristisch” değildir; daha çok pragmatik bir servis çerçevesidir. Tam da bu nedenle medya türü yaklaşımı anlamlıdır: Sabit uç noktalarınızı koruyabilir ve aynı zamanda kontratları evrimleştirebilirsiniz. Önemli olan, versiyonu her zaman değerlendirmek, merkezi bir yerde karara bağlamak ve sonucu servis bağlamınıza aktarmaktır.
Accept-Header yaklaşımı ne zaman başarısız olur?
Pratikte önceden ele alınması gereken üç tipik kırılma noktası vardır:
- Proxy-Policy’leri: Bazı Reverse Proxy’ler/WAF kuralları Accept header’ını normalleştirir veya filtreler. Bu durumda API’niz sessizce varsayılana döner. Çözüm: Proxy kurallarını açıkça inceleyin, gerekirse
X-Api-Versiongibi bir cabeğe başvurun. - İstemci Kütüphaneleri: Bazı HTTP istemcileri kendi Accept header’larını atar ve değerleri geçersiz kılar. Çözüm: Contract versiyonunu opsiyonel bir sorgu parametresi olarak da desteklemek (sadece yedek olarak) veya sunucu tarafında Accept header’ını toleranslı/parçalı olarak parse etmek.
- Önbellek/Proxy Caching: Ara katmanlar header’ları veya vary header listesini dikkate almadan cevapları cache’leyebilir. Çözüm: Vary header’larını doğru ayarlamak ve cache katmanlarının versiyon header’ı göz önünde bulundurmasını sağlamak.
Accept başlığına göre varye edilmeli (Vary: Accept), aksi takdirde sürüm 1’i sürüm 2 istemcilerine döner. Çözüm: Vary bilinçli olarak ayarlamak veya API seviyesinde önbelleklelemeyi devre dışı bırakmak.Kaynak parçacığı: Request-Context, Correlation-ID, Version und Error-Mapping
Kod, mevcut RemObjects-Serverprojekte içine entegre edilebilecek şekilde kasıtlı olarak ayrılmıştır: küçük bir Context katmanı, API sürümünü (Accept’ten) okuyan bir ayrıştırıcı, bir Correlation-ID mekanizması ve merkezi bir Exception-Mapping. Terimler:
- Correlation-ID: Her istek için benzersiz bir kimlik; yanıtta geri döner ve loglarda referanslanır.
- Exception-Mapping: Dahili Delphi istisnalarının, kararlı ve istemci tarafından işlenebilir hata nesnelerine (HTTP durumu dahil) çevrilmesi.
- Contract-Version: JSON sözleşmesinin sürümü; davranışı ve alanları belirler.
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;
// Örneğin beklenen: 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;
// Üretimde dahili ayrıntılar, SQL veya dosya yolları gösterilmez.
// Debug/Stage için bu yapılandırmayla genişletilebilir.
begin
if E is EApiError then
Exit(E.Message);
if E is EArgumentException then
Exit('Geçersiz parametreler.');
Exit('Dahili hata.');
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ç: „herhangi bir yerde Threadlocal içinde“ yerine kararlı bir isteK bağlamı
Bu küçük kod parçası kasıtlı olarak ayırır: TApiContext, iletmek istediğiniz asgari durumdur. RemObjects SDK’da pek çok şey Sunucu-/Channel-bağlamı üzerinden yürür. Heterojen projelerde (ör. ek worker thread’ler, DB kuyruğu, arka plan işlevleri) durumu açıkça iletmek, örtük thread-local değişkenlerden genellikle daha sağlamdır; çünkü eşzamanlılık ve bağlam değişimlerini daha görünür kılar.
Önkoş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 filtrelenebilir veya birleştirilebilir.
Dikkat edilmesi gerekenler: Accept üzerinden 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 anlamlıdır; ancak bu, monitoring’de görünür olmalıdır (ör. log-uyarısı „Version defaulted“).
Varyantlar: Versiyonlamayı tercih ederek X-Api-Version üzerinden yapmak isterseniz: Parser aynıdır, sadece kaynağı başka bir header olur. Gateway’ler açısından bu bazen daha kolay kontrol edilebilir.
RemObjects SDK’ya entegrasyon: Servis girişinde Correlation-ID ve Exception-Mapping
Asıl etki, mekanizmayı sunucunuzun kenarında tutarlı şekilde uyguladığınızda ortaya çıkar: bir kez istek girişinde header’lardan okumak, bir kez exception çıkışında kararlı bir yanıta çevirmek. Hosting’e bağlı olarak (ör. RO-HTTP-Server, IIS-hosting, kendi yönettiğiniz Windows-/Windows- ve Linux-Services) somut hook noktaları değişir; prensip aynıdır: bağlam oluştur, iş mantığını çağır, exception’ları merkezi olarak eşleştir.
RemObjects projelerinde genellikle her servis metodunda doğrudan çalışılır. Bu başlangıçta iyi ölçeklenir, fakat işletmede sorun çıkar: Her metod logging ve hata işleyişini farklı uygular. Temiz bir ayrım, standartlaştırılmış bir servis tabanı veya bir Dispatcherdır.
Pratik akış (bilinçli olarak kısa ve uygulamaya yakın)
- İstek header’ından
X-Correlation-IDile Correlation-ID’yi oku; yoksa sunucuda oluştur (örn. GUID). - Contract-Version’ı
Accept‚ten oku (veyaX-Api-Version‚den). - İstek başlangıcını logla: metod, yol, Correlation-ID, uzak IP; süre ölçümünü başlat.
- İş mantığını çalıştır; veritabanı erişimlerini mümkünse transaksiyonel olarak kapsülle.
- Exception’ı yakala: HTTP durumunu belirle, JSON hata nesnesi oluştur, Response-Header’a
X-Correlation-IDekle. - İstek sonunu logla: durum, süre, gerekirse hata kodu.
Sunucuda threading: Neden bağlam disiplini olmadan Correlation-ID değersizleşir
Yaygın bir Delphi kenar durumu: Servis metodu asenkron işler tetikler (ör. rapor üretimi, import, bir DMS’ye push). Bu durumda orijinal istek thread’i, daha sonra log satırlarını yazan thread olmayabilir. Correlation-ID yalnızca „başlangıçta“ biliniyorsa takip edilebilirlik bozulur.
Pragmatik kural: İstek thread’inde kalmayan her şeye bağlam açıkça iletilmelidir. Bu daha uzun parametre listeleri gibi görünse bile karşılığını verir. Alternatif olarak, global değişkenler veya gizli singleton’lar yerine, işçilere kasıtlı olarak verilecek net tanımlı bir bağlam nesnesiyle çalışın.
RemObjects-/Delphi-sunucularında tipik kırılma noktaları:
- İş parçacığı başına DB bağlantıları: BDE-Ablosung mit nativer Anbindung-bağlantıları otomatik olarak iş parçacığı güvenli şekilde paylaşılamaz. Bir bağlantı havuzu ya da her iş parçacığı için ayrı bir bağlantı, „küresel bir bağlantı“ndan sıklıkla daha mantıklıdır.
- İşlem sınırları: Bir isteğin içinde birlikte olması gereken birden fazla adım varsa, işlem aynı mantıksal birimde kalmalıdır. Asenkron işler kazara aynı işlem içinde devam etmemelidir.
- İptal: İstemci bağlantıyı keserse (Proxy zaman aşımı, tarayıcının kapanması) sunucu çoğu zaman çalışmaya devam eder. Arka plan işlerinin bu durumda hâlâ anlamlı olup olmadığını bilinçli olarak değerlendirin.
Veri erişimi ve hata kodları: 409 „aynı zamanda 500“ değildir
Entegrasyon projelerinde temiz hata eşlemesi kozmetik ötesidir. Karşı tarafın (ERP-Connector, ETL işi, Müşteri portalı) doğru tepki verip veremeyeceğini belirler. Delphi/RemObjects ortamlarında işe yaradığı görülen birkaç pratik ilke:
- 400 Bad Request: Doğrulama hataları, eksik/geçersiz parametreler, JSON ayrıştırılamıyor. Önemli: İstek gövdesi 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 geçerli ama yasak“ anlamındadır.
- 404: Kaynak yok. Güvenlik açısından dikkat: Her zaman bir şeyin var olduğunu açığa vurmayın.
- 409 Conflict: Alanla ilgili çatışma (ör. versiyon çakışması, „durum bu işlemi izin vermiyor“, iş açısından anlamlı bir benzersiz anahtar ihlali).
- 422 Unprocessable Content: Söz dizimi olarak her şey uygunsa ama alan doğrulaması başarısız oluyorsa (her ekip 422 kullanmayabilir, ancak genellikle 400’ten daha nettir).
- 500: Temiz şekilde sınıflandıramadığınız her şey. Buna „DB down“, „Timeout“, „Unhandled Exception“ da dahildir.
Delphi-özgü bir ipucu: Birçok veritabanı hatası genel istisna olarak yukarı çıkar. Veri erişim katmanında bilinen durumları hedefleyip bunları EApiError olarak dönüştürmek faydalıdır. Burada önemli olan: SQL parçalarını ya da dahili tablo/sütun isimlerini istemci mesajına taşımamak. Bu detaylar log’a, yanıtın içine değil.
Hata ayıklama hilesi: „Contract Snapshot“ ile yeniden üretilebilir hatalar
Sıradışı ama işletmede son derece yardımcı: Hatalarda (veya belirli Correlation-ID’ler için kasıtlı olarak) İstek Başlıkları + İstek Gövdesi içeren bir „Snapshot“ı bir debug-spool dosyasına kaydedin. Bu, sürekli bir logging yöntemi değildir (veri koruma/hacim), üretime yakın ortamdan zor yeniden üretilebilir vakaları incelemek için kontrollü bir araçtır.
Önemli: Bir snapshot asla filtrelenmemiş Auth başlıkları, tokenlar ya da kişisel veriler saklamamalıdır. Pratikte bu şu anlama gelir: Redaction (maskelenme) ve etkinleştirme yalnızca bir özellik bayrağı veya beyaz liste üzerinden yapılmalıdır (örn. sadece belirli Correlation-ID’ler, kısa zaman pencereleri).
Pratikte temiz uygulama: Eksiltme yerine maskeleme
Gerçek entegrasyonlarda „kritik“ alanlar genellikle hata ayıklamak için gereken alanlardır (örn. tanımlayıcılar). Toplu olarak tamamen çıkarmak yerine maskeleme daha iyidir: token’ları kısmen değiştirin, e-postada yalnızca alan adını bırakın, IBAN’da yalnızca son birkaç haneyi tutun. Böylece vaka yeniden üretilebilir kalır, dosya sisteminde gereksiz veriler yayılmamış olur. Buna ek olarak snapshot net bir şekilde debug-ögesi olarak işaretlenmeli ve tanımlı bir saklama süresine sahip olmalıdır.
Güvenlik ve İşletim: Header iletimi, Proxy zincirleri ve zaman aşımları
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:
- Uzak IP: X-Forwarded-For’a körü körüne güvenmeyin. Yalnızca güvenilir proxy’lerden alın; aksi halde doğrudan soket IP’sini kullanın. İşletim el kitaplarında hangi hopların güvenilir (trusted) olduğu belirtilmelidir.
- Zaman aşımları: Eğer proxy 30 saniye, backendiniz ise 2 dakika gerekiyorsa, hayalet istekler oluşturursunuz. Zincir boyunca zaman aşımlarını tutarlı şekilde belirleyin ve karar verin: eşzamanlı istek mi yoksa iş-pattern’i (202 Accepted + durum uç noktası) mi kullanacaksınız.
- Correlation-ID: Correlation-ID’yi response header’larına ekleyin, böylece yöneticiler bunu loglardan ve istemci tarafından birleştirebilir. Bir gateway kendi request-ID’lerini kullanıyorsa: her iki ID’yi de loglayın ve ilişkilendirin.
- Hata metinleri: Üretim ortamında dahili ayrıntılar gösterilmemelidir. Debug ayrıntıları yalnızca kontrollü olarak (stage/feature-flag) sağlanmalı ve şüphe durumunda sadece loglarda tutulmalıdır.
Değerlendirme: RemObjects SDK’nın burada avantajı neden olabilir
In Delphi-Ökosistemlerde 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:
- Net sınırları: Servis metotları açıkça tanımlıdır; sözleşmeler (Contracts) sürümlendirilebilir.
- Taşıma ve serileştirme: JSON kullanabilirsiniz, ayrıca kurulumunuza bağlı olarak diğer mesaj formatlarını da destekleyebilirsiniz; iş mantığını karıştırmadan.
- İşletim: Barındırma seçenekleri ve mevcut Windows- ve Linux-servisler ile entegrasyon planlanabilir; temiz roll-out’lar dahil.
Gösterilen yaklaşım, günlük kullanımda sıklıkla eksik kalan parçaları tamamlar: tek tip hata objeleri, deterministik sürümlendirme ve korelasyonu mümkün kılan logging. Özellikle uzun ömürlü, müşteriye özel kurumsal yazılımlarda, bu yaklaşımla güncellemeler ve harici sistem entegrasyonlarında zamandan tasarruf edersiniz.
Sonuç: Çaba değer mi — ve yaklaşım nerede tersine döner?
Katma değer, REST arayüzünüzün sadece „çalışması“ değil, sürdürülebilir şekilde işletilebilir olmasıyla ortaya çıkar: stabil JSON sözleşmeleri, URL karmaşasına yol açmadan sürümlendirme, izlenebilir hatalar ve tahmine dayalı olmayan hata ayıklama. Tam da bu noktada, bağlam (Context), Correlation-ID ve merkezi istisna eşlemesi (Exception-Mapping) ile olan yaklaşım RemObjects SDK içinde güçlüdür.
Kullanım sınırları: Sadece entegrasyon ortağı olmayan tek, kısa ömürlü bir uç noktanız varsa, Media-Type-Versionierung çabuk aşırı mühendislik gibi gelir. Snapshot-Logging de yalnızca redaction (gizleme) ve etkinleştirmeyi disiplinli şekilde uygularsanız anlamlıdır. Ve: Proxy yığını başlıkları „optimize“ ediyor veya kaldırıyorsa, önce altyapıyı düzeltmelisiniz; aksi halde yanlış katmanı debug edersiniz.
Eğer mevcut bir Delphi-sunucu altyapısını modernize ediyorsanız veya proses odaklı bir yazılım çözümünü ERP/DMS/CRM içine düzgün şekilde entegre etmeniz gerekiyorsa, bu mekanizmalar genellikle „testte çalışıyor“ ile „işletmede çalışıyor“ arasındaki farkı yaratır.
Uzmanlık alanında, Delphi REST-API ve REST-sunucusu ve Remobjects Sdk Delphi entegrasyonlar, veri akışları ve sürekli geliştirme sorunsuz bir şekilde birlikte çalışması gerektiğinde önemli bir rol oynar.
Sonraki adım
Konu gerçek bir projeye dönüştüğünde, mimari, mevcut sistemler ve işletme erken dönemde birlikte değerlendirilmelidir.
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 daha sonra ortaya çıkan sonuçlar olarak ertelenmez.
- Hangi yolun ekonomik ve işletme açısından sürdürülebilir olduğunu erken görürsünüz.