🔢 B8 · Status Kodu ve ResponseEntity: 200/201/204

B8 · Status Kodu ve ResponseEntity: `ResponseEntity`, geliştiricinin **response'un tam kontrolünü** eline aldığı araçtır: sadece gövdeyi değil, status kodunu ve header'ları da bi

`ResponseEntity`, geliştiricinin **response'un tam kontrolünü** eline aldığı araçtır: sadece gövdeyi değil, status kodunu ve header'ları da bilinçli seçer. Başarı bile tek bir kod değildir: **200 OK** = "işte sonuç" (GET), **201 Created** = "yeni kaynak oluşturdum, adresi de Location header'ında" (POST), **204 No Content** = "yaptım ama dönecek gövde yok" (DELETE/bazı PUT). Peki hepsi "başarı" ise 200 dönsek olmaz mı, istemci nasılsa çalışır? Çoğu zaman "çalışır" ama sözleşme bozulur: bir POST 201 yerine 200 dönerse, istemci `Location` header'ından yeni kaydın adresini alan otomasyon zinciri kırılır; bir DELETE 204 yerine 200 + boş gövde dönerse, gövdeyi ayrıştırmaya çalışan istemci hata verebilir. Java'da bunun karşılığı `return bug;` (Spring 200 varsayar) ile `ResponseEntity.status(201).header("Location", ...).body(bug)` arasındaki bilinçli farktır. QA açısından status kodu **anlamsal bir sözleşmedir**: tester yalnızca "başarılı mı" değil, "DOĞRU başarı kodu mu" diye test eder — çünkü yanlış ama 2xx bir kod, istemci otomasyonlarını sessizce bozan bir contract hatasıdır.

Doğru Başarı Kodunu Seçmek

**🐞 Defect Doğum Anı — POST 201 yerine 200 dönerse (Location yok)** **Kod:** `@PostMapping public Bug create(...) { return service.create(req); }` — Spring bunu 200 döner, `Location` header'ı YOKtur. **Ne olur:** Yeni bir bug oluşturan otomasyon testi, sonraki adımda kaydı okumak için `Location` header'ındaki adresi bekler (`/api/v1/bugs/42`). Header gelmediği için zincir kırılır; ya da yeni id'yi gövdeden okumaya çalışan farklı bir istemci, 200 gördüğü için "oluşturma değil güncelleme oldu" sanır. **Neden sinsi:** Kayıt gerçekten oluşur, gövde döner, status 2xx'tir — hızlı bir bakışta "başarılı". Sorun yalnızca 201/Location'a GÜVENEN bir istemci zinciri koştuğunda ortaya çıkar; basit testler bunu ıskalar. **Tester nerede yakalar:** POST sonrası status'un tam 201 olduğunu VE `Location` header'ının yeni kaydın adresini verdiğini doğrulayan bir contract testiyle. "2xx = geçti" varsayımı bu hatayı gizler.

🎬 200 mü 201 mi? Location Header'ı ve Kırılan Zincir

Bir otomasyon zinciri: önce POST ile bug oluştur, sonra dönen adresten onu GET ile oku. Her şey doğru status koduna bağlı.

Doğru yol — POST 201 Created döner ve Location header'ı yeni kaydın adresini verir: /api/v1/bugs/42. Zincir bu adresi kullanır.

Yanlış yol — geliştirici `return bug` yazdı, Spring 200 döner ve Location header'ı YOKtur. Kayıt oluşur ama adres kaybolur.

Sonraki request Location'dan adresi almaya çalışır ama header yok — zincir kırılır. Test "oluşturma başarısız" gibi görünmez, "sonraki adım null" der.

Ders — 2xx yeterli değil; DOĞRU 2xx gerekir. POST=201+Location, GET=200, DELETE=204. Tester status kodunu ve Location'ı ayrıca doğrular.

Hangi Operasyon Hangi Kodu Döner?

Okuma başarılıysa 200 + gövde. Kayıt yoksa 404 (200 + boş gövde DEĞİL).

Oluşturma 201 + Location header'ı döner; istemci yeni kaydın adresini buradan alır.

DELETE → 204 No Content…

Silme başarılıysa 204 (gövdesiz). İstemci gövde ayrıştırmaya çalışmamalıdır.