---
title: 'İstek ve Yanıt Gövdeleri'
source: 'https://academia.sh/tr/kurslar/api-tasarimi/istek-ve-yanit-govdeleri'
course: 'Web API Tasarımı'
language: tr
updated: '2026-08-17T18:06:45+00:00'
license: 'CC BY-SA 4.0'
---

# İstek ve Yanıt Gövdeleri

Gövde biçiminin sözleşme değeri: alan adlandırmada tek yazım, büyük tamsayıların ve ondalık tutarların sessiz kaybı, tarihlerin bölge bilgisiyle yazılması, eksik alan ile boş değerin ayrılması ve koleksiyon yanıtının zarf kararı.

Bir önceki ders yanıtın makine tarafından okunan bölümünü — durum kodunu — yerine oturttu.
Geriye gövde kaldı. Ölçüm sunucularında gövdeler gelişigüzel yazıldı: bir yerde yalnız bir
kimlik alanı, başka yerde hata adı ve sınır değeri, bir başka yerde tablonun sütun adları
olduğu gibi.

Gövde biçimi, durum kodu kadar sözleşmedir. Alan adı değişirse istemci kırılır; bir sayının
nasıl yazıldığı yanlış seçilirse istemci kırılmaz — daha kötüsü olur, sessizce yanlış değer
okur. Bu ders gövde kararlarını sıralar ve ikisi ölçülerek gösterilir: sayıların ve
tarihlerin çözümlenmesi, bir de eksik alanla boş değerin ayrılması.

## Gövde Bir Sözleşmedir

İlk karar alan adlandırmasıdır ve içeriğinden çok tutarlılığı önemlidir. Bir yanıtta
`verilis`, başka bir yanıtta `verilisTarihi`, üçüncüsünde `verilis_tarihi` geçiyorsa
istemci her uç nokta için ayrı bir çözümleme yazar. Kurs boyunca tek bir yazım seçilir ve
değişmez.

İkinci karar, gövdenin veritabanı şemasının kopyası olmamasıdır. Bir önceki derste ödünç
kaydı doğrudan `SELECT *` sonucuyla döndürülmüştü; bu, sütun adlarını sözleşmeye
sızdırır. Sütun adı değiştiğinde ya da bir sütun eklendiğinde istemci etkilenir. Doğru
davranış, gösterimi açıkça kurmaktır: hangi alanların dışarı çıkacağı yazılır, geri kalanı
içeride kalır.

Üçüncü karar numaralandırma değerlerine ilişkindir. Bir ödünç kaydının durumu `acik`,
`iade_edildi`, `gecikti` gibi sabit kodlarla bildirilir; "Açık", "İade Edildi" gibi
görüntülenecek metinlerle değil. Metin dile ve yazıma bağlıdır, kod ise sözleşmenin
parçasıdır ve karşılaştırılabilir. Ekranda ne yazacağı istemcinin kararıdır.

## Sayılar: Sessiz Kayıp

Kütüphane katalogunda her kopyanın bir demirbaş numarası vardır ve bu numara başka bir
sistemden gelir. Gecikme ücreti de ondalıklı bir tutardır. İkisi de JSON'da sayı olarak
yazıldığında ne olduğuna bakalım.

```bash
# JSON govdesindeki iki sessiz kayip: buyuk tamsayi ve bicimsiz tarih.
node -e '
const govde = `{"demirbas": 9007199254740993, "ucret": 0.1, "ek": 0.2}`;
const c = JSON.parse(govde);
console.log("gonderilen demirbas :", govde.match(/\d{16}/)[0]);
console.log("cozulen  demirbas   :", String(c.demirbas));
console.log("esit mi              :", String(c.demirbas) === govde.match(/\d{16}/)[0]);
console.log("ucret + ek           :", c.ucret + c.ek);
'
echo "---"
TZ=Europe/Istanbul node -e '
for (const d of ["12/03/2026", "2026-03-12", "2026-03-12T00:00:00", "2026-03-12T00:00:00+03:00"])
  console.log(d.padEnd(28), new Date(d).toISOString());
'
```

```
gonderilen demirbas : 9007199254740993
cozulen  demirbas   : 9007199254740992
esit mi              : false
ucret + ek           : 0.30000000000000004
---
12/03/2026                   2026-12-02T21:00:00.000Z
2026-03-12                   2026-03-12T00:00:00.000Z
2026-03-12T00:00:00          2026-03-11T21:00:00.000Z
2026-03-12T00:00:00+03:00    2026-03-11T21:00:00.000Z
```

İlk üç satır bir kimliğin çözümleme sırasında bozulduğunu gösteriyor. JSON'un sayı tipinin
kesinliği çözümleyicinin kayan noktalı sayı temsiline bağlıdır; Bilgisayarlar Nasıl Çalışır
kursunda tanımlanan çift duyarlıklı temsilin tamsayı sınırı aşıldığında en yakın
temsil edilebilir değere yuvarlanır. Sunucuya `...993` gönderilir, istemcide `...992`
okunur. Hiçbir hata üretilmez; kayıp, o kimlikle yapılan bir sorgu boş dönene kadar
görünmez.

Dördüncü satır aynı kökten gelen ikinci sorundur: `0.1 + 0.2` işleminin sonucu tam
`0.3` değildir. Para tutarları ondalıklı sayı olarak taşındığında toplama işlemleri
biriken hatalar üretir.

Her iki sorunun da çözümü aynıdır: **hesaplanmayacak sayılar dizgi olarak taşınır.**
Kimlikler dizgidir — zaten üzerlerinde aritmetik yapılmaz. Para tutarları ya en küçük
birim cinsinden tamsayı olarak (kuruş) ya da dizgi olarak taşınır ve karşılaştırma o
biçimde yapılır. Bir kimliğin sayı görünümlü olması onu sayı yapmaz.

## Tarihler: Biçimsiz Dizgi Yoktur

Çıktının ikinci bölümü tarih alanlarını gösteriyor ve dört satırın dördü de farklı bir
tuzağa denk gelir. Bu blok `TZ=Europe/Istanbul` ile çalıştırılmıştır; yerel saat dilimi
değiştiğinde üçüncü satırın sonucu da değişir — asıl sorun da budur.

`12/03/2026` yazımı 12 Mart olarak değil, 2 Aralık olarak çözümlendi. Gün ve ayın hangi
sırada yazıldığı yazımın kendisinden anlaşılmadığı için çözümleyici kendi varsayımını
uyguladı. Sözleşmede böyle bir alan bulunuyorsa istemcilerin yarısı sekiz ay ileriyi okur.

`2026-03-12` yazımı bölgesiz bir gündür ve gün ortası UTC olarak alındı.
`2026-03-12T00:00:00` yazımı ise bölge bilgisi taşımadığı için **yerel** saat sayıldı ve
UTC'ye çevrildiğinde bir gün geriye kaydı. Aynı dizgi, iki farklı makinede iki farklı ana
karşılık gelir. Yalnızca son satır — bölge farkını açıkça yazan biçim — her yerde aynı anı
gösterir.

Kural şudur: **bir zaman anı bildiriliyorsa bölge farkı yazılır.** Ödünç verme anı gibi
alanlar tam biçimde taşınır. Yalnızca takvim günü bildiriliyorsa — son iade günü gibi —
alanın adı bunu söylemeli ve gösterimde saat hiç bulunmamalıdır. İki türü aynı alanda
karıştırmak, yaz saati geçişlerinde bir günlük kaymalar üretir.

## Yok ile Boş Aynı Şey Değil

Kısmi güncellemede istemci üç şey söyleyebilir: bu alanı şu değere getir, bu alanı boşalt,
bu alana dokunma. JSON gövdesinde ilk ikisi değerle, üçüncüsü **alanın hiç bulunmamasıyla**
bildirilir. Bu ayrımı gözetmeyen bir yazım, "boşalt" isteğini hiçbir zaman
gerçekleştiremez.

```js
// alan-sunucusu.mjs — kismi guncellemede "yok" ile "bos" ayrimi
import { createServer } from "node:http";
import { DatabaseSync } from "node:sqlite";

const db = new DatabaseSync("kutuphane.db");
const govdeOku = (istek) => new Promise((coz) => {
  let veri = ""; istek.on("data", (p) => (veri += p));
  istek.on("end", () => coz(veri ? JSON.parse(veri) : {}));
});
const yanitla = (yanit, kod, nesne) => {
  yanit.writeHead(kod, { "content-type": "application/json; charset=utf-8" });
  yanit.end(JSON.stringify(nesne));
};

const sunucu = createServer(async (istek, yanit) => {
  const yol = new URL(istek.url, "http://127.0.0.1").pathname;
  const gevsek = /^\/gevsek\/oduncler\/(\d+)$/.exec(yol);
  const kesin = /^\/kesin\/oduncler\/(\d+)$/.exec(yol);
  if (istek.method !== "PATCH" || !(gevsek || kesin))
    return yanitla(yanit, 404, { hata: "yol_yok" });

  const id = Number((gevsek ?? kesin)[1]);
  const g = await govdeOku(istek);
  const mevcut = db.prepare("SELECT * FROM odunc WHERE id = ?").get(id);
  if (!mevcut) return yanitla(yanit, 404, { hata: "odunc_yok" });

  if (gevsek) {
    // Bos deger ile eksik alani ayirmayan yazim: null hicbir zaman yazilamaz.
    const yeni = g.iade ?? mevcut.iade;
    db.prepare("UPDATE odunc SET iade = ? WHERE id = ?").run(yeni, id);
  } else {
    // Alanin govdede bulunup bulunmadigina bakilir; degeri ayrica degerlendirilir.
    if ("iade" in g) db.prepare("UPDATE odunc SET iade = ? WHERE id = ?").run(g.iade, id);
  }
  yanitla(yanit, 200, db.prepare("SELECT id, iade FROM odunc WHERE id = ?").get(id));
});

sunucu.listen(8479, "127.0.0.1", () => console.log("alan sunucusu 127.0.0.1:8479"));
```

```bash
# Ayni uc govde iki yazima gonderilir; iade alaninin son degeri karsilastirilir.
rm -f kutuphane.db && sqlite3 kutuphane.db < sema.sql
node alan-sunucusu.mjs & sunucu=$!
sleep 0.4

yolla() { curl -sS -X PATCH -H 'content-type: application/json' -d "$2" "http://127.0.0.1:8479$1"; echo; }
for kip in gevsek kesin; do
  echo "--- $kip ---"
  yolla "/$kip/oduncler/2" '{"iade":"2026-03-20"}'
  yolla "/$kip/oduncler/2" '{"uye":"U-1001"}'
  yolla "/$kip/oduncler/2" '{"iade":null}'
done

kill $sunucu
```

```
alan sunucusu 127.0.0.1:8479
--- gevsek ---
{"id":2,"iade":"2026-03-20"}
{"id":2,"iade":"2026-03-20"}
{"id":2,"iade":"2026-03-20"}
--- kesin ---
{"id":2,"iade":"2026-03-20"}
{"id":2,"iade":"2026-03-20"}
{"id":2,"iade":null}
```

Her iki kipin ilk iki satırı aynı: değer atanır, alan gövdede yoksa korunur. Üçüncü satır
ayrışır. Gevşek yazımda `iade` alanı boşaltılamadı; çünkü `??` işleci `null` değeri
"verilmemiş" sayar ve eski değeri geri koyar. Yanlışlıkla kapatılmış bir ödünç kaydını
düzeltmenin yolu yoktur; istemci isteği gönderir, 200 alır, hiçbir şey değişmez.

Kesin yazımda ayrım gövdenin yapısından okunur: alan gövdede varsa değeri — `null` dahil —
uygulanır, yoksa dokunulmaz. Bu davranış sözleşmede yazılı olmalıdır, çünkü istemcinin
"alanı göndermemek" ile "alanı boş göndermek" arasındaki farkı bilerek kullanması gerekir.

Aynı ayrım yanıt tarafında da geçerlidir. Bir alanın değeri yoksa `null` olarak
gönderilmesi ile hiç gönderilmemesi farklı şeyler söyler: birincisi "bu alan var ama boş",
ikincisi "bu alan bu gösterimde yok". Kurs boyunca tek bir kural seçilir; yanıtta bazen
`null`, bazen alanın hiç bulunmaması istemciyi her iki olasılığı da yazmaya zorlar.

## Koleksiyon Yanıtı: Çıplak Dizi mi, Zarf mı

Koleksiyon yanıtının en kısa biçimi çıplak bir dizidir. Kısa olması tek üstünlüğüdür.
Diziye ek bilgi konamaz: kaç kayıt olduğu, bir sonraki sayfanın nerede başladığı, sorgunun
hangi ölçütle çalıştığı. Bu bilgiler başlıklara taşınabilir, ama başlıklar gövdeyle
birlikte önbelleklenmeyebilir ve okunmaları istemci tarafında ayrı bir iş gerektirir.

**Zarf** yazımı gövdeyi bir nesneye sarar: kayıtlar bir alanda, sayfalama bilgisi başka bir
alanda durur. Maliyeti bir düzey fazladan iç içelik, kazancı yanıtın kendini
anlatabilmesidir. Sonraki iki ders — sayfalama ve bağlantı odaklı yanıtlar — bu alanı
kullanacağı için kurs boyunca zarf yazımı seçilir.

Tekil kaynak yanıtları ise sarılmaz. `/oduncler/2` adresinden dönen gövde doğrudan ödünç
kaydının gösterimidir; onu bir alana sarmak, hiçbir bilgi eklemeden her istemciye bir
düzey daha inme yükü getirir.

## Özet

- Alan adlandırmada tek yazım biçimi seçilir ve kurs boyunca değişmez; gövde veritabanı
  şemasının kopyası değil, açıkça kurulmuş bir gösterimdir.
- Üzerinde aritmetik yapılmayacak sayılar — kimlikler — dizgi olarak taşınır; ölçümde
  `9007199254740993` değeri çözümlendiğinde `9007199254740992` oldu ve hata üretilmedi.
- Para tutarları ondalıklı sayı olarak taşınmaz; en küçük birim cinsinden tamsayı ya da
  dizgi kullanılır.
- Zaman anı bildiren alanlar bölge farkını açıkça yazar; bölgesiz yazımlar makineye göre
  farklı ana karşılık gelir ve gün/ay sırası belirsiz yazımlar aylarca kayabilir.
- Kısmi güncellemede alanın gövdede bulunup bulunmadığı ile değerinin boş olması ayrı
  bilgilerdir; ayrımı gözetmeyen yazımda bir alanı boşaltmak olanaksızdır.
- Koleksiyon yanıtları zarfla sarılır, çünkü sayfalama ve gezinme bilgisi gövdenin içinde
  taşınacaktır; tekil kaynak yanıtları sarılmaz.

## Sonraki Adım

Zarfın boş duran alanı bir sonraki dersin konusudur. Ödünç koleksiyonu üç satırla
sınırlıyken tüm kayıtları tek yanıtta göndermek sorun değildi; on binlerce kayıtta bu
yanıt hem sunucuyu hem istemciyi taşır. Kayıtları parça parça vermek gerekir ve bunun
birden çok yolu vardır. Sonraki ders atlama tabanlı sayfalamayı kurar, araya kayıt
eklendiğinde okuyucunun bir kaydı iki kez görmesini ya da hiç görmemesini gerçekten
üretir, sonra aynı senaryoda kaymayan bir yöntem gösterir.
