Validation ve Hata Yönetimi — İstemci Ne Görüyor?
Önce şunu oku: Exception Yönetimi — Hata Yukarı Çıkarken Ne Kaybolur
30 saniyede özet
Alanın üstüne @Positive yazmak yetmez; @Valid demezsen kimse kontrol etmez. Hataları da tek bir yerde doğru koda çevir: bozuk istek 400, iş kuralı ihlali 409 gibi 4xx, kod hatası sade bir 500.
Kapıya “yalnızca davetliler” yazan bir tabela asmak, kapıda kimseyi durdurmaz. Birinin davetiyelere bakması gerekir.
-
Bayt: Adet alanının üstüne @Positive yazdım. Artık eksi adet giremez!
-
Sen: Veritabanında eksi adetli bir sipariş duruyor ama.
-
Bayt: Nasıl olur? Kural orada yazıyordu!
-
Bayt: Kapıya tabela asmak, kapıya bekçi koymak değildir.
Alanın üstünde @Positive yazıyor. Yine de veritabanında -1 adetlik bir sipariş var ve stok yetmediğinde istemci 500 alıyor. İkisi de aynı yerden geliyor: kısıtları ve hataları kimin ele aldığı belli değil.
Kontrol kapıda başlar
Bean ValidationKısıtları alanlara anotasyonla yazma standardı (Jakarta Validation): @NotBlank, @Positive, @Email. Kısıtları kendisi çalıştırmaz; @Valid ya da bir validator tetikler.Sözlükte gör → anotasyonları (@NotBlank, @Positive, @Email) bir kuralı yalnızca yazar, kendi başına kontrol etmez. Kapıda kontrolü yapacak birini ayrıca çağırman gerekir.
OrderRequest.quantity alanında @Positive var ama controller parametresi yalnızca @RequestBody OrderRequest. -1 gelirse ne olur? Cevabı göster
Sipariş -1 adetle kaydedilir. Kısıtı tetikleyen @Valid yok; anotasyon sadece bir süs olarak kalır.
Adım adım oku
- OrderRequest'in adet alanında @Positive yazıyor: kapıda bir tabela.
- Controller parametresinde @Valid yoksa kimse tabelaya bakmaz; -1 içeri girer ve veritabanına kadar ulaşır.
- @Valid kapıya bir bekçi koyar: kural ihlal edilince istek kapıda 400 ile geri çevrilir.
- Her hata kendi koduna çevrilir: bozuk istek 400, çakışma 409; 500 yalnızca sunucunun kendi hatasıdır.
@PostMapping("/orders")ResponseEntity<OrderResponse> place(@Valid @RequestBody OrderRequest request) { … }Kapıdaki görevli @Valid’dir: kurala uymayan isteği controller’a varmadan yakalar ve MethodArgumentNotValidException fırlatır.
Kafam karıştı, daha basit anlat
@Positive gibi etiketler kapıya asılmış kurallardır. Kapıda bekçi yoksa kimse onları okumaz. @Valid, o bekçiyi kapıya koymaktır.
`OrderRequest` sınıfındaki `quantity` alanında `@Positive` var. Controller metodu `place(@RequestBody OrderRequest req)`. -1 gelirse ne olur?
Her hatanın bir kodu var
| Durum | Kod | Kimin sorunu |
|---|---|---|
| Bozuk JSON, geçersiz alan | 400 | İstemci düzeltmeli |
| Kaynak yok | 404 | İstemci başka bir şey istemeli |
| Stok yok, e-posta kayıtlı | 409 | İstek geçerli, durum uygun değil |
| NullPointerException | 500 | Bizim hatamız |
“Stok yetmedi” bir sunucu arızası değildir, bu yüzden 500 dönülmez. 500 alan istemci yeniden dener, alarm sistemi de boş yere çalar.
Kafam karıştı, daha basit anlat
Hata kodu, kimin hatası olduğunu söyler. 4xx “senin isteğinde bir sorun var” demektir, 5xx “bizde bir şey bozuldu”. Stok yetmediyse bu bir arıza değildir.
Sipariş servisi stok yetersizse `OutOfStockException` fırlatıyor ve istemci 500 alıyor. Doğru durum kodu hangisi?
Her durumu en uygun HTTP durum koduna yerleştir.
Kendin gör
Doğrulama ve hata yönetimi — istemci ne görüyor?
Tohum 194483POST /orders
{ "sku": "SKU-42", "quantity": -1 }- JSON → OrderRequestbekliyor
- @Validbekliyor
- OrderService.placebekliyor
- Exception handlingbekliyor
Şu an ne oldu?
POST /orders
Gövde: { "sku": "SKU-42", "quantity": -1 }
Görevler0/4
Geçersiz bir siparişi 201 ile kaydettiraçık
İpucu
Kısıt anotasyonları yerinde. Onları kim tetikliyor?
Stok yetersizliğini 409 Conflict olarak döndüraçık
İpucu
Boot varsayılanı bu istisnayı tanımıyor ve 500 dönüyor.
quantity = -1 için istemciye hangi alanın hatalı olduğunu söyleaçık
İpucu
Doğrulama açık olmalı ve hata gövdesinde alan listesi olmalı.
Bir hatanın iç ayrıntılarını istemciye sızdıraçık
İpucu
Beklenmeyen bir hata ve istisnayı olduğu gibi yazan bir handler.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. 400 geldi, ama gövde hangi alanın hatalı olduğunu söylemiyor.
- Hata yönetimini advice yap. Aynı 400, şimdi alan listesiyle.
- @Valid’i kapat. 201 — ve -1 adetlik bir sipariş.
- “Stoktan fazla adet” seç. Varsayılanda 500, advice ile 409.
- “Kodda bir hata” ve
ex.toString()seç. Paket ve alan adları istemcide.
Bu endpoint'e geçersiz siparişler kaydediliyor ve hatalarda istemci her zaman 200 alıyor. Hangi satırlar sorunlu?
Satır satır: tek bir advice
Hangi hatanın hangi koda dönüşeceğine tek bir yerde, bir @RestControllerAdviceBütün controller'lardan çıkan istisnaları tek yerde HTTP cevabına çeviren sınıf. İçindeki @ExceptionHandler metotları hangi istisnanın hangi koda eşleneceğini söyler.Sözlükte gör → içinde karar verilir. Cevabın gövdesi için standart bir biçim olan ProblemDetailHTTP API hataları için RFC 9457 standardındaki gövde: type, title, status, detail, instance. Spring 6'da hazır bir sınıf olarak gelir.Sözlükte gör → (RFC 9457) kullanılır.
Hangi istisna hangi cevaba
@RestControllerAdviceclass ApiErrors extends ResponseEntityExceptionHandler { @ExceptionHandler(OutOfStockException.class) ProblemDetail outOfStock(OutOfStockException ex) { return ProblemDetail.forStatusAndDetail(CONFLICT, ex.userMessage()); } @ExceptionHandler(Exception.class) ProblemDetail unexpected(Exception ex) { var ref = UUID.randomUUID().toString().substring(0, 8); log.error("unexpected error ref={}", ref, ex); return ProblemDetail.forStatusAndDetail(INTERNAL_SERVER_ERROR, "Beklenmeyen hata. Referans: " + ref); }}Debug
Spring ResponseEntityExceptionHandler'dan türemek: bozuk JSON ve doğrulama gibi framework istisnaları zaten doğru 4xx ve ProblemDetail'e çevrilir.
Sol/sağ ok tuşlarıyla da gezebilirsin.
İstemciye anlamı, log’a ayrıntıyı ver. İkisini bir referans kimliği bağlar.
Kafam karıştı, daha basit anlat
Bütün hatalar tek bir masaya gelir ve orada karşılığı olan koda çevrilir. Kullanıcıya kısa bir açıklama gider, ayrıntılar log’da kalır.
Spring 6'daki `ProblemDetail` nedir?
Spring Boot 3'te `spring.mvc.problemdetails.enabled=true` ayarı ne yapar?
Tuzaklar
ex.getMessage()’ı cevaba yazmak. Hata mesajındaki sınıf, tablo ve alan adları kullanıcıya kadar gider; sistemin içi dışarıdan görünür.
Her katmanda loglamak. Aynı hata service’te ve advice’ta loglanırsa bir hata iki alarm olur. Kenarda bir kez logla.
Controller’da catch (Exception). Hatalar “her şey yolunda” (200) cevabına dönüşür ve asıl sebep kaybolur. Controller yalnızca her şeyin yolunda gittiği durumu anlatsın.
@Valid’in iç içe nesnelere inmemesi. OrderRequest içindeki Address alanının kısıtları için o alanın da @Valid taşıması gerekir.
Bir @ExceptionHandler(Exception.class) metodu `ex.getMessage()`'ı cevap gövdesine yazıyor. Bunun asıl riski ne?
Aşağıdaki örnek bir bankanın havale API’sinden: talimat kaydında kurallar, IBAN kontrol hanesini doğrulayan özel bir kısıt, tek bir @RestControllerAdvice ve her hatada aynı biçimde ProblemDetail.
Derinleş · Havale talimatı: doğrulama ve RFC 9457 hata cevabı 6 dosya · ~132 satır · ilk okumada atlayabilirsin
Kendini sına
@Positive gibi anotasyonlar kendi başlarına kontrol yapar.
Hata yönetimini tek bir @RestControllerAdvice'ta topladın. Hangi hatalar ERROR seviyesinde loglanmalı?
Aklında kalacak üç şey
- 1 Kısıt anotasyonları kendiliğinden çalışmaz. @Valid @RequestBody, isteği controller'a ulaşmadan kontrol eder.
- 2 İş kuralı ihlali bir sunucu hatası değildir. Stok yetmiyorsa cevap 500 değil, 409 gibi bir 4xx olmalıdır.
- 3 Kullanıcıya ne olduğunu, log'a ayrıntıyı ver. Hata mesajını olduğu gibi cevaba yazmak sistemin içini dışarı gösterir.
5 kart sonraki derste seni bekliyor