İçeriğe geç
academia.sh

Ders 12 / 34

İ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ı.

İçindekiler

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.

# 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.

// 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"));
# 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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat