Net-Base Dergi

09.06.2026

REST API RemObjects SDK ile: JSON uç noktalarını düzgün sürümleme ve hata ayıklama (Delphi kaynak kodu örneği)

RemObjects SDK ile Delphi içinde işletmede istikrarlı kalan bir REST API nasıl kurulur: sağlam JSON sözleşmeleri, URL karmaşasına yol açmayan sürümleme, Correlation-ID'nin tüm katmanlarda taşınması, merkezi hata eşlemesi, zorlu hata ayıklama vakaları için snapshot-logging ve uygulamaya dönük pratik ipuçları...

09.06.2026

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-Version gibi 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.
  • Caching: Yanıt önbellekleme devredeyse, önbellek 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.
    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;
    // Ö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)

    1. İstek header’ından X-Correlation-ID ile Correlation-ID’yi oku; yoksa sunucuda oluştur (örn. GUID).
    2. Contract-Version’ı Accept‚ten oku (veya X-Api-Version‚den).
    3. İstek başlangıcını logla: metod, yol, Correlation-ID, uzak IP; süre ölçümünü başlat.
    4. İş mantığını çalıştır; veritabanı erişimlerini mümkünse transaksiyonel olarak kapsülle.
    5. Exception’ı yakala: HTTP durumunu belirle, JSON hata nesnesi oluştur, Response-Header’a X-Correlation-ID ekle.
    6. İ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.

    Proje veya modernizasyon projesini Net-Base ile görüşün.

    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.

    Gönderiyi paylaş

    Bu gönderiyi doğrudan paylaş

    LinkedIn, X, XING, Facebook, WhatsApp ve e-posta hemen kullanılabilir. Instagram için bağlantıyı ve kısa metni doğrudan hazırlıyoruz.

    E-posta

    Instagram yeni bir sekmede açılır. Bağlantı ve kısa metin önceden panoya kopyalanır.