🧯 B7 · Exception Handling: @RestControllerAdvice

B7 · Exception Handling: @RestController: Exception handling, API'nin **itfaiye ve tercüman ekibidir**: kodun içinde bir şey ters gittiğinde (kayıt yok, iş kuralı ihlali, beklenm

Exception handling, API'nin **itfaiye ve tercüman ekibidir**: kodun içinde bir şey ters gittiğinde (kayıt yok, iş kuralı ihlali, beklenmeyen çökme) ham Java exception'ını istemcinin anlayacağı temiz bir HTTP response'una ÇEVİRİR. `@RestControllerAdvice`, tüm controller'lar için tek merkezî hata çevirmenidir: `BugNotFoundException` → 404, `IllegalStateException` → 409, geri kalan her şey → 500. Peki her metotta try-catch yazsak olmaz mı, neden merkezî bir yapı? Çünkü hata çevirisi controller'lara dağılırsa biri 404 döner, biri 500, biri hiç yakalamaz ve istemci tutarsız response'larla karşılaşır; merkezî advice, hata→status eşlemesinin TEK doğru kaynağıdır. Java'da bunun karşılığı global bir `try-catch` değil, `@ExceptionHandler` metotlarıdır; her exception türü kendi status kodu ve gövdesiyle eşlenir. QA açısından bu katman, **hata response'larının sözleşmesidir**: bir tester yalnızca "mutlu yol"u değil, hata durumlarının da doğru status + anlamlı mesaj döndüğünü test etmelidir — çünkü kötü bir hata response'u (500 yerine 200, ya da stack trace sızması) hem istemciyi yanıltır hem güvenlik açığı olur.

Merkezî Hata Çevirmeni

**🐞 Defect Doğum Anı — ham exception istemciye sızarsa** **Kod:** `@RestControllerAdvice` YOK ya da genel bir handler tüm exception'ları yakalayıp `ex.getMessage()`'ı olduğu gibi 500 gövdesine koyuyor. **Ne olur:** Bir `SQLException` veya `NullPointerException` istemciye ham haliyle döner: response gövdesinde tam **stack trace**, veritabanı tablo/sütun adları, hatta dosya yolları görünür. Kötü niyetli biri için bu bir hazine haritasıdır (sistem içini ifşa eder); ayrıca istemci "ne oldu" diye net bir mesaj alamaz. **Neden sinsi:** Mutlu yol testlerinde hiç görünmez — her şey 200/201 döner. Sızıntı yalnızca bir hata tetiklendiğinde ortaya çıkar ve çoğu ekip hata response'larını hiç test etmez. **Tester nerede yakalar:** Kasıtlı olarak hata tetikleyerek (olmayan id, geçersiz veri, bozuk JSON) response gövdesini inceleyince — stack trace, SQL, iç yol görürse bu hem bir bilgi sızıntısı (güvenlik) hem de kötü bir hata sözleşmesidir.

🎬 404 mü 500 mü, Yoksa Sızıntı mı? Bir Hatanın Çevirisi

@RestControllerAdvice

Stack trace sızıntısı

Kod içinde bir exception fırladı: olmayan bir bug istendi (BugNotFoundException). Şimdi bu ham hata istemciye nasıl dönecek?

Merkezî advice VARSA: exception yakalanır ve temiz bir 404 + anlamlı mesaja çevrilir. İstemci "kayıt yok" diye net bir response alır.

Advice YOKSA: ham exception istemciye sızar — response gövdesinde tam stack trace, SQL, tablo adları, dosya yolları görünür.

Bu sızıntı kötü niyetli biri için bir harita: sistem içini (DB şeması, teknoloji, yollar) ifşa eder ve bir sonraki saldırıyı kolaylaştırır.

Ders — Hata response'ları da bir sözleşmedir: doğru status + anlamlı mesaj, ham iç detay SIZDIRMADAN. Tester hataları kasıtlı tetikleyip gövdeyi denetler.

Hata → Status Eşlemesi

BugNotFoundException beklenen bir durumdur; advice bunu 404'e çevirir, 500'e değil.

Zaten CLOSED gibi bir çatışma 409 Conflict'e eşlenir — istemci "durum uyuşmazlığı"nı anlar.