---
title: 'HTTP Yöntemlerinin Doğru Kullanımı'
source: 'https://academia.sh/tr/kurslar/api-tasarimi/http-yontemlerinin-dogru-kullanimi'
course: 'Web API Tasarımı'
language: tr
updated: '2026-08-17T18:06:45+00:00'
license: 'CC BY-SA 4.0'
---

# HTTP Yöntemlerinin Doğru Kullanımı

Yöntemin taşıdığı sözler: güvenli yöntemin veriyi değiştirmemesi ve etkisiz yöntemin tekrarlandığında tek etki üretmesi; her özelliğin veritabanındaki satır sayısı sayılarak sınanması, PATCH'in artırım ve atama yüzleri, PUT ile POST arasındaki kimlik seçimi.

Bir önceki ders yolu kaynağın kimliğine ayırdı ve "ne yapılacağını yöntem söyler" diyerek
geçti. O cümle bir görev dağılımından fazlasını içerir. HTTP yöntemi yalnızca sunucuya
gönderilen bir emir değildir; istemcinin, önbelleklerin ve aradaki her bileşenin isteğe
bakarak güvendiği bir sözdür.

Bu ders o sözleri tanımlar ve her birini ölçer. Ölçüm biçimi tektir: aynı istek arka arkaya
iki kez gönderilir ve veritabanındaki satır sayısı ile alan değerleri her adımda sayılır.
Bir yöntemin sözünü tuttuğu ya da tutmadığı, tartışmayla değil sayılarla belirlenir.

## Yöntem Bir Ad Değil, Bir Söz

İnternet Nasıl Çalışır kursu iki özelliği tanıtmıştı. **Güvenli** (safe) bir yöntem sunucu
tarafında gözlenebilir bir değişiklik bırakmaz; okuma yapar, yazma yapmaz. **Etkisiz**
(idempotent) bir yöntem, aynı isteğin bir kez ya da arka arkaya beş kez gönderilmesi
arasında sunucunun son durumu bakımından fark bırakmaz. İkisi bağımsızdır: her güvenli
yöntem etkisizdir, ama her etkisiz yöntem güvenli değildir.

| Yöntem | Güvenli | Etkisiz | Gövde taşır |
|---|---|---|---|
| GET | evet | evet | hayır |
| HEAD | evet | evet | hayır |
| OPTIONS | evet | evet | hayır |
| POST | hayır | hayır | evet |
| PUT | hayır | evet | evet |
| PATCH | hayır | tasarıma bağlı | evet |
| DELETE | hayır | evet | genellikle hayır |

Bu özellikler sunucunun iç işi değildir; dışarıya verilmiş taahhütlerdir. Bir önbellek,
GET yanıtını saklayıp bir sonraki isteğe sunucuya hiç gitmeden yanıt verebilir — çünkü
GET'in bir yan etkisi olmadığına güvenir. Bir istemci, ağ hatası aldığında PUT isteğini
yeniden gönderebilir — çünkü tekrarın zararsız olduğuna güvenir. Uygulama Mimarisi
kursundaki istek katmanı bu güveni **yeniden denenebilirlik** olarak kodlamıştı.

Taahhüt tutulmazsa kırılan şey sunucu değil, sunucuya güvenen taraflardır.

## Güvenlilik: Okuma Veriyi Değiştirmez

Aşağıdaki servis, ölçüm için bilerek bir kusur taşır: ödünç iadesi hem doğru yoldan hem de
okuma görünümlü bir adresten yapılabilir.

```js
// yontem-sunucusu.mjs — yontem ozelliklerini olcmek icin kurulmus servis
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(nesne === null ? "" : JSON.stringify(nesne));
};

const sunucu = createServer(async (istek, yanit) => {
  const yol = new URL(istek.url, "http://127.0.0.1").pathname;
  const y = istek.method;
  const kitap = /^\/kitaplar\/([^/]+)$/.exec(yol);
  const odunc = /^\/oduncler\/(\d+)$/.exec(yol);
  const iadeYolu = /^\/oduncler\/(\d+)\/iade$/.exec(yol);

  if (y === "GET" && yol === "/oduncler")
    return yanitla(yanit, 200, db.prepare("SELECT * FROM odunc").all());

  // YANLIS: okuma gorunumlu bir adres veriyi degistiriyor.
  if (y === "GET" && iadeYolu) {
    db.prepare("UPDATE odunc SET iade = '2026-03-12' WHERE id = ? AND iade IS NULL")
      .run(Number(iadeYolu[1]));
    return yanitla(yanit, 200, { sonuc: "iade alindi" });
  }

  if (y === "POST" && yol === "/oduncler") {
    const g = await govdeOku(istek);
    const s = db.prepare("INSERT INTO odunc (uye, isbn, verilis, iade) VALUES (?,?,?,NULL)")
                .run(g.uye, g.isbn, g.verilis);
    return yanitla(yanit, 201, { id: Number(s.lastInsertRowid) });
  }

  if (y === "PUT" && kitap) {
    const g = await govdeOku(istek);
    // Tam degisim: gonderilen gosterim kaydin tamamini belirler.
    const varMi = db.prepare("SELECT 1 FROM kitap WHERE isbn = ?").get(kitap[1]);
    db.prepare(`INSERT INTO kitap (isbn, ad, yazar, yil, sube, kopya) VALUES (?,?,?,?,?,?)
                ON CONFLICT(isbn) DO UPDATE SET
                  ad=excluded.ad, yazar=excluded.yazar, yil=excluded.yil,
                  sube=excluded.sube, kopya=excluded.kopya`)
      .run(kitap[1], g.ad, g.yazar, g.yil, g.sube, g.kopya);
    return yanitla(yanit, varMi ? 200 : 201, db.prepare("SELECT * FROM kitap WHERE isbn = ?").get(kitap[1]));
  }

  if (y === "PATCH" && kitap) {
    const g = await govdeOku(istek);
    if ("kopyaEkle" in g)  // artirim: onceki degere baglidir
      db.prepare("UPDATE kitap SET kopya = kopya + ? WHERE isbn = ?").run(g.kopyaEkle, kitap[1]);
    if ("kopya" in g)      // atama: onceki degerden bagimsizdir
      db.prepare("UPDATE kitap SET kopya = ? WHERE isbn = ?").run(g.kopya, kitap[1]);
    return yanitla(yanit, 200, db.prepare("SELECT isbn, kopya FROM kitap WHERE isbn = ?").get(kitap[1]));
  }

  if (y === "DELETE" && odunc) {
    db.prepare("DELETE FROM odunc WHERE id = ?").run(Number(odunc[1]));
    return yanitla(yanit, 204, null);
  }

  yanitla(yanit, 404, { hata: "yol_yok" });
});

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

Şimdi bu servise, sayfadaki bağlantıları sırayla izleyen bir istemci gelsin. Böyle bir
istemci yalnızca GET yollar; bağlantının arkasında ne olduğunu bilmez.

```bash
# Okuma gorunumlu adresi bir baglanti tarayicisi izlerse ne olur?
rm -f kutuphane.db && sqlite3 kutuphane.db < sema.sql
sqlite3 kutuphane.db "ALTER TABLE kitap ADD COLUMN kopya INTEGER NOT NULL DEFAULT 1;"
node yontem-sunucusu.mjs & sunucu=$!
sleep 0.4

acik() { sqlite3 kutuphane.db "SELECT COUNT(*) FROM odunc WHERE iade IS NULL;"; }
printf 'baslangicta acik odunc      : %s\n' "$(acik)"

# Sayfadaki butun baglantilari izleyen bir istemci: yalnizca GET yollar.
for yol in /oduncler /oduncler/1/iade /oduncler/3/iade; do
  printf 'GET %-22s -> %s\n' "$yol" "$(curl -sS -o /dev/null -w '%{http_code}' "http://127.0.0.1:8476$yol")"
done
printf 'GET sonrasi acik odunc      : %s\n' "$(acik)"

kill $sunucu
```

```
yontem sunucusu 127.0.0.1:8476
baslangicta acik odunc      : 2
GET /oduncler              -> 200
GET /oduncler/1/iade       -> 200
GET /oduncler/3/iade       -> 200
GET sonrasi acik odunc      : 0
```

İki açık ödünç kaydı, hiçbir kullanıcı iade işlemi yapmadan kapandı. İstemci kural
çiğnemedi: bağlantı izleyen bir program için GET, sorulması serbest bir sorudur. Kuralı
çiğneyen sunucudur.

Bu kusurun ölçeği tasarım hatasının ötesine geçer. Sayfa önizlemesi üreten bir araç, bağlantı
denetleyen bir betik, adres çubuğuna yazılanı önceden getiren bir tarayıcı — hepsi aynı
varsayımla çalışır. Şema iki tabloyla kurulmuş bir örnekte iki satır kaybolur; gerçek bir
katalogda kayıp, izlenen bağlantı sayısı kadardır.

Doğru tasarımda `/oduncler/{id}/iade` gibi bir adres hiç açılmaz; iade, bir önceki derste
gösterildiği gibi `PATCH /oduncler/{id}` isteğiyle yapılır. Adres okuma görünümlüyse yöntem
de okuma olmalıdır ve tersi.

## Etkisizlik: Aynı İsteği İki Kez Göndermek

Etkisizlik ölçümü doğrudandır. Her yöntem arka arkaya iki kez çağrılır; iki çağrının
sonucu bir çağrınınkinden farklıysa yöntem etkisiz değildir.

```bash
# Her yontem iki kez cagrilir; her cagridan sonra veritabanindaki etki sayilir.
rm -f kutuphane.db && sqlite3 kutuphane.db < sema.sql
sqlite3 kutuphane.db "ALTER TABLE kitap ADD COLUMN kopya INTEGER NOT NULL DEFAULT 1;"
node yontem-sunucusu.mjs & sunucu=$!
sleep 0.4

T=http://127.0.0.1:8476
say() { printf '%-26s odunc=%s  kopya=%s\n' "$1" \
  "$(sqlite3 kutuphane.db 'SELECT COUNT(*) FROM odunc;')" \
  "$(sqlite3 kutuphane.db "SELECT kopya FROM kitap WHERE isbn='978-0131103627';")"; }

say "baslangic"
curl -sS -o /dev/null $T/oduncler; curl -sS -o /dev/null $T/oduncler
say "GET x2"
for i in 1 2; do curl -sS -o /dev/null -X POST -H 'content-type: application/json' \
  -d '{"uye":"U-1002","isbn":"978-0131103627","verilis":"2026-03-10"}' $T/oduncler; done
say "POST x2"
for i in 1 2; do curl -sS -o /dev/null -X PUT -H 'content-type: application/json' \
  -d '{"ad":"C Programlama Dili","yazar":"Ritchie","yil":1988,"sube":"S-01","kopya":4}' \
  $T/kitaplar/978-0131103627; done
say "PUT x2"
for i in 1 2; do curl -sS -o /dev/null -X PATCH -H 'content-type: application/json' \
  -d '{"kopyaEkle":2}' $T/kitaplar/978-0131103627; done
say "PATCH artirim x2"
for i in 1 2; do curl -sS -o /dev/null -X PATCH -H 'content-type: application/json' \
  -d '{"kopya":6}' $T/kitaplar/978-0131103627; done
say "PATCH atama x2"
printf '%-26s %s %s\n' "DELETE x2 (durum kodu)" \
  "$(curl -sS -o /dev/null -w '%{http_code}' -X DELETE $T/oduncler/2)" \
  "$(curl -sS -o /dev/null -w '%{http_code}' -X DELETE $T/oduncler/2)"
say "DELETE x2"

kill $sunucu
```

```
yontem sunucusu 127.0.0.1:8476
baslangic                  odunc=3  kopya=1
GET x2                     odunc=3  kopya=1
POST x2                    odunc=5  kopya=1
PUT x2                     odunc=5  kopya=4
PATCH artirim x2           odunc=5  kopya=8
PATCH atama x2             odunc=5  kopya=6
DELETE x2 (durum kodu)     204 204
DELETE x2                  odunc=4  kopya=6
```

Satırlar tek tek okunmalıdır.

**GET x2** hiçbir sayıyı değiştirmedi: yöntem güvenlidir, dolayısıyla etkisizdir.

**POST x2** ödünç sayısını üçten beşe çıkardı. İki istek, iki kayıt üretti. POST'un tanımı
budur: koleksiyona yeni bir üye eklenir ve kimliği sunucu belirler. Ağ koptuğu için
yeniden gönderilen bir POST, ikinci bir ödünç kaydı doğurur. Bu, tasarımın bozukluğu
değil, POST'un doğasıdır; çözümü dokuzuncu dersin konusudur.

**PUT x2** kopya sayısını 4 yaptı ve orada bıraktı. Gönderilen gövde kaydın tamamını
belirlediği için ikinci istek birinciyle aynı son durumu üretir. Kaç kez gönderildiği
sonucu değiştirmez.

**PATCH artırım x2** kopya sayısını 4'ten 8'e çıkardı. Gövde `kopyaEkle` alanını taşıyor,
yani yeni değer eskisine bağlı. Bu bir PATCH kusuru değildir; PATCH'in etkisizliğinin
gövdeye bağlı olduğunun kanıtıdır. **PATCH atama x2** aynı yöntemle 6 değerini üretti ve
ikinci istek onu değiştirmedi.

Buradan çıkan tasarım kuralı nettir: kısmi güncelleme gövdesi artırım değil atama üzerine
kurulursa yöntem etkisiz olur. Artırım gerçekten gerekiyorsa — sayaç, bakiye, stok —
etkisizlik gövdeyle sağlanamaz; dokuzuncu dersteki anahtar düzeneği gerekir.

**DELETE x2** iki isteğe de 204 döndürdü ve ödünç sayısı beşten dörde indi, ikinci silmede
düşmedi. Etkisizliğin tanımı budur: son durum aynıdır. İkinci isteğe 404 döndürmek de
savunulabilir bir seçimdir ve etkisizliği bozmaz, çünkü etkisizlik yanıt kodunun değil
sunucu durumunun özelliğidir. Hangisinin seçileceği bir sonraki dersin konusudur.

## PUT ile POST Arasındaki Seçim

İkisi de yazma yapar; ayrımı yapan şey kimliği kimin belirlediğidir.

**POST**, kimliği sunucunun belirlediği durumda kullanılır. İstemci `/oduncler`
koleksiyonuna bir gövde yollar, sunucu yeni kaydın kimliğini üretir ve yanıtta bildirir.
İstemci isteği göndermeden önce hedef adresi bilemez.

**PUT**, kimliği istemcinin bildiği durumda kullanılır. `/kitaplar/978-0131103627` adresine
yapılan PUT, o adreste bir kaynak yoksa oluşturur, varsa gönderilen gösterimle değiştirir.
Yukarıdaki sunucu bu ayrımı durum koduna da yansıtır: kayıt yoksa 201, varsa 200.

PUT'un ikinci özelliği çoğu zaman gözden kaçar: **gönderilmeyen alanlar boşalır.** PUT bir
tam değişimdir, kısmi güncelleme değildir. Gövdede `yazar` alanı yoksa kaydın yazarı
silinmiş sayılır. Bu davranış istenmiyorsa doğru yöntem PATCH'tir. Bir servisin PUT'u kısmi
güncelleme gibi işletmesi, istemcinin etkisizlik varsayımını bozmaz ama sözleşmeyi
belirsizleştirir: aynı gövde iki serviste iki farklı sonuç verir.

## Yöntem Uyuşmazlığı

Bir adres var olduğu hâlde istenen yöntemi desteklemiyorsa — örneğin `/uyeler/U-1001/durum`
kaynağına DELETE gelirse — yanıt "bulunamadı" olmamalıdır. Kaynak duruyor, yöntem
uygulanamıyor. HTTP bunun için ayrı bir durum kodu tanımlar ve yanıtın hangi yöntemlerin
kabul edildiğini bildirmesini bekler. Yukarıdaki sunucu bu ayrımı yapmıyor; eşleşmeyen her
şeye 404 dönüyor. Bir sonraki ders bu eksiği ölçüp kapatır.

## Özet

- Yöntem, sunucunun dışarıya verdiği bir taahhüttür: güvenli yöntem gözlenebilir değişiklik
  bırakmaz, etkisiz yöntem tekrarlandığında son durumu değiştirmez.
- Okuma görünümlü bir adresin veri değiştirmesi, bağlantı izleyen her programı silici hâline
  getirir; ölçümde üç GET isteği iki açık ödünç kaydını kapattı.
- POST etkisiz değildir ve olmaya da çalışmaz; kimliği sunucu ürettiği için her istek yeni
  kayıt doğurur.
- PUT tam değişimdir ve etkisizdir; gönderilmeyen alanlar boşalır, bu yüzden kısmi
  güncelleme için uygun değildir.
- PATCH'in etkisizliği gövdeye bağlıdır: atama biçimindeki gövde etkisiz, artırım
  biçimindeki gövde değildir — ölçümde kopya sayısı 4'ten 8'e çıktı.
- DELETE etkisizdir; ikinci silme isteğinin farklı durum kodu döndürmesi bu özelliği
  bozmaz, çünkü etkisizlik sunucu durumunun özelliğidir.

## Sonraki Adım

Bu derste yanıtların durum kodları büyük ölçüde varsayıldı: oluşturmada 201, silmede 204,
eşleşmeyen her şeyde 404. Oysa bunların her biri bir seçimdir ve seçim yanlış yapıldığında
istemci hatayı fark etmez. Bir kaynağın var olmaması ile ona erişim izni olmaması aynı kod
mu döner? İş kuralının reddettiği bir istek ile gövdesi bozuk bir istek nasıl ayrılır?
Sonraki ders aynı senaryo kümesini iki farklı sunucuya sorar, ürettikleri kodları yan yana
koyar ve yanlış eşlemenin istemcide ne ürettiğini gösterir.
