---
title: 'Eski Nesil Protokoller'
source: 'https://academia.sh/tr/kurslar/api-tasarimi/eski-nesil-protokoller'
course: 'Web API Tasarımı'
language: tr
updated: '2026-08-17T18:06:44+00:00'
license: 'CC BY-SA 4.0'
---

# Eski Nesil Protokoller

Zarf tabanlı protokollerin tasarımı ve mirası: başlık ile gövdenin ayrılması, işlem adının zarfın içinde taşınması, hatanın taşıma katmanı yerine zarfta bildirilmesi, zarf maliyetinin kayıt sayısıyla değişimi ve bugünkü arayüz kararlarının hangilerinin buradan devralındığı.

Önceki ders tek uç nokta üzerinden, gövdede taşınan bir seçim belgesiyle çalışan bir arayüz
kurdu. Bu düzenek yeni değildir. Aynı fikri — tek adres, tek yöntem, işlemi gövdede
adlandırma — çok daha önce kuran bir protokol ailesi vardır ve bugünkü arayüz kararlarının
bir kısmı doğrudan oradan devralınmıştır.

Bu ailenin ortak yapı taşı **zarftır** (envelope): iletinin tamamını saran, içinde bir
**başlık** ve bir **gövde** bulunduran bir kap. **SOAP** bu yapının en yaygın bilinen örneğidir
ve XML üzerine kurulmuştur; `Envelope`, `Header`, `Body` ve `Fault` ögeleri bu dersin
kuracağı `Zarf`, `Baslik`, `Govde` ve `Hata` ögelerinin karşılığıdır.

## Zarfın Fikri

Zarfın çözdüğü sorun şudur: bir iletiyi taşıyan protokolden bağımsız olarak, iletinin kendi
üstverisini yanında taşımak. HTTP başlıkları bu işi zaten yapar, ama zarf tasarımı HTTP'ye
bağlı değildir — aynı zarf bir ileti kuyruğu ya da bir posta iletisi üzerinden de gidebilir.
Bunun bedeli ve kazancı bir arada durur.

Zarfın üç yapısal kararı vardır:

- **Başlık kesişen ilgileri taşır:** izlem kimliği, kimlik bilgisi, işlem adı. Ara katmanlar
  bu bölümü okuyup gövdeyi hiç çözümlemeden karar verebilir.
- **Gövde yükü taşır:** işleme özgü veri. Yükün biçimi işlemden işleme değişir.
- **Hata da gövdeye girer:** başarısızlık, taşıma katmanının durum koduyla değil, gövdedeki
  bir hata ögesiyle bildirilir.

## Küçük Bir Gerçekleştirim

Aşağıdaki dosya zarfı kuran ve okuyan işlevleri içerir. XML ayrıştırıcısı iç içe ögeleri bir
nesneye çevirir; öznitelik ve ad alanı desteklemez, çünkü ders için gereken şey yapının
kendisidir.

```js
// zarf.mjs — kucuk XML ayristirici ve zarf kuruculari.
// Zarf/Baslik/Govde/Hata ogeleri SOAP'taki Envelope/Header/Body/Fault karsiliklaridir.
const kacisla = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
const cozKacis = (s) => s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");

export const xmlYaz = (ad, icerik) =>
  typeof icerik === "object" && icerik !== null
    ? `<${ad}>${Object.entries(icerik).map(([a, d]) => xmlYaz(a, d)).join("")}</${ad}>`
    : `<${ad}>${kacisla(icerik)}</${ad}>`;

// Ayristirici: ic ice ogeleri bir nesneye cevirir. Ayni adli kardesler diziye toplanir.
export const xmlOku = (metin) => {
  const oge = /<(\w+)>([\s\S]*?)<\/\1>/g;
  const cikti = {};
  let e, bulundu = false;
  while ((e = oge.exec(metin))) {
    bulundu = true;
    const deger = /<\w+>/.test(e[2]) ? xmlOku(e[2]) : cozKacis(e[2]);
    if (e[1] in cikti) cikti[e[1]] = [].concat(cikti[e[1]], deger);
    else cikti[e[1]] = deger;
  }
  return bulundu ? cikti : cozKacis(metin);
};

export const zarfla = (islem, izlem, govde) =>
  `<Zarf>${xmlYaz("Baslik", { Islem: islem, Izlem: izlem })}` +
  `<Govde>${Object.entries(govde).map(([a, d]) => xmlYaz(a, d)).join("")}</Govde></Zarf>`;

export const hataZarfi = (kod, neden, ayrinti) =>
  `<Zarf><Baslik><Islem>Hata</Islem></Baslik><Govde>` +
  xmlYaz("Hata", { Kod: kod, Neden: neden, Ayrinti: ayrinti }) + `</Govde></Zarf>`;

// Ara katmanin okudugu tek yer: govdeyi hic cozumlemeden baslik alinabilir.
export const basligiAyikla = (zarf) => xmlOku(/<Baslik>[\s\S]*?<\/Baslik>/.exec(zarf)[0]).Baslik;
```

```js
// sunucu.mjs — tek uc nokta, tek yontem: islem adi zarfin basligindadir.
import { createServer } from "node:http";
import { xmlOku, zarfla, hataZarfi, basligiAyikla } from "./zarf.mjs";

const KITAPLAR = {
  "978-0262033848": { isbn: "978-0262033848", ad: "Algoritmalara Giris", yazar: "Cormen", sayfa: 1312 },
  "978-0201896831": { isbn: "978-0201896831", ad: "Programlama Sanati", yazar: "Knuth", sayfa: 650 },
};

createServer(async (istek, yanit) => {
  yanit.sendDate = false;
  let ham = "";
  for await (const p of istek) ham += p;
  const baslik = basligiAyikla(ham);                  // yonlendirme: yalniz baslik okunur
  const govde = xmlOku(ham).Zarf.Govde;
  let cikti;
  if (baslik.Islem === "KitapGetir") {
    const kitap = KITAPLAR[govde.KitapIstek.isbn];
    cikti = kitap ? zarfla("KitapGetirYanit", baslik.Izlem, { Kitap: kitap })
                  : hataZarfi("Istemci", "kitap bulunamadi", `isbn: ${govde.KitapIstek.isbn}`);
  } else cikti = hataZarfi("Istemci", "bilinmeyen islem", baslik.Islem);
  yanit.setHeader("Content-Type", "text/xml; charset=utf-8");
  yanit.setHeader("Content-Length", Buffer.byteLength(cikti));
  // Hata da basarili istek de ayni durum koduyla doner: hata tasima katmaninda degil zarftadir.
  yanit.writeHead(200).end(cikti);
}).listen(8481, "127.0.0.1", () => console.log("zarf ucu 127.0.0.1:8481/servis"));
```

## Ölçüm

```js
// olc.mjs — zarf tabanli protokolun uc ozelligini olcer
import { zarfla, xmlOku, basligiAyikla } from "./zarf.mjs";

const cagir = async (islem, izlem, govde) => {
  const istek = zarfla(islem, izlem, govde);
  const y = await fetch("http://127.0.0.1:8481/servis",
    { method: "POST", headers: { "Content-Type": "text/xml" }, body: istek });
  const metin = await y.text();
  return { kod: y.status, gonderilen: Buffer.byteLength(istek), alinan: Buffer.byteLength(metin),
           metin, cozulen: xmlOku(metin).Zarf.Govde };
};

console.log("-- 1) tek uc nokta, islem adi zarfta --");
const iyi = await cagir("KitapGetir", "izl-1", { KitapIstek: { isbn: "978-0262033848" } });
console.log(`  istek : ${zarfla("KitapGetir", "izl-1", { KitapIstek: { isbn: "978-0262033848" } })}`);
console.log(`  yanit : ${iyi.metin}`);
console.log(`  HTTP ${iyi.kod}  gonderilen=${iyi.gonderilen} B  alinan=${iyi.alinan} B`);

console.log("\n-- 2) zarf maliyeti: ayni veri JSON ve zarf biciminde --");
const KAYIT = { isbn: "978-0262033848", ad: "Algoritmalara Giris", yazar: "Cormen", sayfa: 1312 };
for (const adet of [1, 5, 20]) {
  const liste = Array.from({ length: adet }, () => KAYIT);
  const json = Buffer.byteLength(JSON.stringify({ Kitap: liste }));
  const zarf = Buffer.byteLength(zarfla("KitapGetirYanit", "izl-1", { Kitap: liste }));
  console.log(`  ${String(adet).padStart(2)} kayit  json=${String(json).padStart(4)} B  ` +
    `zarf=${String(zarf).padStart(4)} B  fark=%${Math.round(100 * (zarf / json - 1))}`);
}

console.log("\n-- 3) hata zarfta, tasima katmaninda degil --");
const kotu = await cagir("KitapGetir", "izl-2", { KitapIstek: { isbn: "978-0000000000" } });
console.log(`  yanit : ${kotu.metin}`);
console.log(`  durum koduna bakan istemci  -> HTTP ${kotu.kod} : "istek basarili"`);
console.log(`  govdeyi cozen istemci       -> ${JSON.stringify(kotu.cozulen.Hata)}`);

console.log("\n-- 4) ara katman govdeyi cozmeden basligi okur --");
for (const z of [iyi.metin, kotu.metin]) {
  const govdeBoyu = /<Govde>[\s\S]*<\/Govde>/.exec(z)[0].length;
  console.log(`  baslik=${JSON.stringify(basligiAyikla(z))}  (govde ${govdeBoyu} B, cozumlenmedi)`);
}
```

```bash
node sunucu.mjs & p=$!
curl -s --retry 20 --retry-connrefused --retry-delay 0 -o /dev/null -X POST \
  -d '<Zarf><Baslik><Islem>x</Islem></Baslik><Govde></Govde></Zarf>' http://127.0.0.1:8481/servis
node olc.mjs
kill $p
```

```
zarf ucu 127.0.0.1:8481/servis
-- 1) tek uc nokta, islem adi zarfta --
  istek : <Zarf><Baslik><Islem>KitapGetir</Islem><Izlem>izl-1</Izlem></Baslik><Govde><KitapIstek><isbn>978-0262033848</isbn></KitapIstek></Govde></Zarf>
  yanit : <Zarf><Baslik><Islem>KitapGetirYanit</Islem><Izlem>izl-1</Izlem></Baslik><Govde><Kitap><isbn>978-0262033848</isbn><ad>Algoritmalara Giris</ad><yazar>Cormen</yazar><sayfa>1312</sayfa></Kitap></Govde></Zarf>
  HTTP 200  gonderilen=142 B  alinan=205 B

-- 2) zarf maliyeti: ayni veri JSON ve zarf biciminde --
   1 kayit  json=  94 B  zarf= 212 B  fark=%126
   5 kayit  json= 426 B  zarf= 620 B  fark=%46
  20 kayit  json=1671 B  zarf=2170 B  fark=%30

-- 3) hata zarfta, tasima katmaninda degil --
  yanit : <Zarf><Baslik><Islem>Hata</Islem></Baslik><Govde><Hata><Kod>Istemci</Kod><Neden>kitap bulunamadi</Neden><Ayrinti>isbn: 978-0000000000</Ayrinti></Hata></Govde></Zarf>
  durum koduna bakan istemci  -> HTTP 200 : "istek basarili"
  govdeyi cozen istemci       -> {"Kod":"Istemci","Neden":"kitap bulunamadi","Ayrinti":"isbn: 978-0000000000"}

-- 4) ara katman govdeyi cozmeden basligi okur --
  baslik={"Islem":"KitapGetirYanit","Izlem":"izl-1"}  (govde 125 B, cozumlenmedi)
  baslik={"Islem":"Hata"}  (govde 116 B, cozumlenmedi)
```

## Ölçümün Söyledikleri

**Zarfın maliyeti sabittir, yükün maliyeti değişkendir.** Tek kayıtta zarf, aynı verinin JSON
biçiminden %126 daha büyüktür; yirmi kayıtta bu fark %30'a iner. Zarfın kendisi — başlık
bölümü, sarmalayan ögeler — kayıt sayısından bağımsız sabit bir maliyettir; kayıt başına
düşen fazlalık ise kapanış etiketlerinden gelir ve her kayıtta tekrarlanır. Küçük ve sık
çağrılarda zarf pahalıdır; büyük yüklerde oran düşer.

**Hatanın zarfta bildirilmesinin bedeli tüketiciye çıkar.** Var olmayan bir kitap istendiğinde
sunucu `200` döndürmüştür. Durum koduna bakan bir istemci isteği başarılı sayar; hatayı
görmek için gövdeyi çözümlemek gerekir. Bu, dördüncü derste anılan **hata sözleşmesinin ikiye
bölünmesi** sorununun kaynağıdır ve GraphQL'in kısmen başarılı yanıtında aynı biçimde geri
gelir.

**Başlık gövdeden bağımsız okunabilir.** Ölçümün son bölümü, yüz baytın üzerindeki gövde
hiç çözümlenmeden başlığın alınabildiğini gösterir. Bu, bir ara katmanın — yönlendirici,
yetkilendirme katmanı, izleme katmanı — yükü anlamadan iş yapabilmesi demektir. Başlığın
taşıdığı **izlem kimliği** aynı isteğin farklı bileşenlerdeki kayıtlarını birbirine bağlar.

## Bırakılan Miras

Zarf tabanlı tasarımın bugüne aktarılan yanları, ölçümde görülen özelliklerin doğrudan
devamıdır.

**Makine okunur sözleşme.** Bu ailenin arayüzleri, hangi işlemlerin bulunduğunu ve her
işlemin girdi–çıktı tiplerini ayrı bir tanım dosyasında yayımlar. Buradan iki alışkanlık
kaldı: sözleşmenin belgeden ayrı, çalıştırılabilir bir dosya olması ve istemci tarafının bu
dosyadan üretilebilmesi. Aynı fikir dördüncü derste şema dosyası olarak, bu kursun ilerleyen
bölümünde ise OpenAPI ve JSON Schema tabanlı tanımlar olarak görülür.

**Yapılandırılmış hata.** Hata ögesindeki üçlü — kod, neden, ayrıntı — bugünkü standart hata
gövdelerinin atasıdır: makinenin okuyacağı bir kod, insanın okuyacağı bir açıklama ve
bağlama özgü bir ek. Bu yapı, hata sözleşmesi dersinde ayrıntılandırılır.

**Kesişen ilgilerin ayrılması.** Kimlik, izlem ve yönlendirme bilgisinin yükten ayrı
tutulması korunmuştur; HTTP tabanlı arayüzlerde bu ayrım zarfın başlığı yerine HTTP
başlıklarında yaşar.

**İşlem adının gövdede taşınması.** Tek uç nokta ve gövdede işlem adı örüntüsü, hem uzak
yordam çağrısı biçemli HTTP arayüzlerinde hem de GraphQL'de sürer.

Devralınmayanlar da bellidir. Taşımadan bağımsız olma isteği, HTTP'nin kendi anlamlarını —
yöntemler, durum kodları, koşullu istekler, önbellek yönergeleri — kullanılmaz kılar. Üçüncü
derste ölçülen önbelleklenebilirlik kazancı, her isteğin `POST` olduğu bir tasarımda elde
edilemez. Metin biçiminin ayrıntı yüküyle birlikte bu, aynı işi daha az bayt ve daha az
katmanla yapan tasarımların yolunu açan asıl nedendir.

## Özet

- Zarf tabanlı protokoller iletiyi başlık ve gövde olarak saran bir kapla çalışır; işlem adı
  adreste değil, zarfın başlığında taşınır ve taşıma katmanı değiştirilebilir sayılır.
- Ölçümde zarfın bayt maliyeti tek kayıtta %126, yirmi kayıtta %30 çıktı: zarfın kendisi
  sabit maliyet, kapanış etiketleri ise kayıt başına tekrarlanan maliyettir.
- Hata zarfın içinde bildirildiği için sunucu başarısız işlemde de `200` döndürür; durum
  koduna bakan istemci hatayı göremez, gövdeyi çözümlemek zorundadır.
- Başlık gövdeden bağımsız okunabildiği için ara katmanlar yükü hiç çözümlemeden yönlendirme,
  yetkilendirme ve izleme yapabilir.
- Devralınan miras dört kalemdir: makine okunur sözleşme dosyası, yapılandırılmış hata
  yapısı, kesişen ilgilerin yükten ayrılması ve tek uç nokta üzerinde adlandırılmış işlem.

## Sonraki Adım

Buraya kadarki beş biçemin ortak varsayımı aynıdır: iletişimi istemci başlatır, sunucu yanıt
verir, alışveriş biter. Kütüphane servisinin bazı işleri bu kalıba girmez. Bir kitabın rafa
dönmesi sunucuda olan bir olaydır ve bunu bekleyen istemciye kimse sormaz. Bir sayım işi
dakikalarca sürer ve isteği açık tutmak bir seçenek değildir. Sonraki ders istek–yanıt
dışındaki iletişim biçimlerini kurar ve ikisini ölçerek karşılaştırır: bir olay akışı ile uzun
yoklama aynı bildirimi kaç bağlantıyla ve ne kadar gecikmeyle ulaştırır?
