- Bir REST isteği Spring'de nasıl işlenir?
- Projeyi kurmak ve ilk endpoint'i yazmak
- Katmanları ayırın: controller, servis, repository
- HTTP metotları ve durum kodları
- Girdi doğrulama ve tutarlı hata yanıtı
- Sayfalama, filtreleme ve test
- Sık yapılan hatalar
- Bu beceriyi Mindset programlarında nerede çalışırsınız?
- Sık sorulan sorular
Spring Boot ile REST API yazmak: controller'dan hata yönetimine adım adım
Spring Boot'ta controller, servis, doğrulama ve hata yönetimiyle bir REST API'yi katmanlı yapıda nasıl yazacağınızı kod örnekleriyle öğrenin.
Bir REST isteği Spring'de nasıl işlenir?
Spring Boot'ta bir HTTP isteği, gömülü sunucuya gelir ve Spring Web MVC'nin ön denetleyicisi (DispatcherServlet) tarafından, adres ve HTTP metoduna uyan controller metoduna yönlendirilir. Controller metodu parametreleri hazırlar: adres değişkenini, sorgu parametresini ya da gövdedeki JSON'u nesneye çevirir. Metodun döndürdüğü nesne de JSON'a dönüştürülerek yanıt gövdesine yazılır. Bu çeviri işini Jackson kitaplığı yürütür ve starter bağımlılığıyla birlikte hazır gelir.
REST API yazmak bu yüzden esas olarak şu işlere dayanır: kaynakları doğru adreslerle modellemek, her isteği doğrulayıp katmanlara ayırarak işlemek ve hataları tutarlı bir biçimde döndürmek. Kod miktarı az, ama her kararın sonucu API'nizi kullanan tarafı etkiler.
Projeyi kurmak ve ilk endpoint'i yazmak
Proje iskeletini Spring Initializr ile oluşturup web ve doğrulama starter'larını seçin; veri katmanı gerekiyorsa JPA'yı ayrıca ekleyin. Başlangıç için ana sınıf ve tek bir controller yeterlidir:
@RestController @RequestMapping("/api/products") class ProductController { private final ProductService servis; ProductController(ProductService servis) { this.service = service; }
@GetMapping("/{id}") ProductResponse get(@PathVariable Long id) { return service.get(id); } }
Burada bağımlılık yapıcı metot üzerinden enjekte ediliyor; bu, test yazarken bağımlılığı kolayca değiştirmeyi sağlar. Adreslerde çoğul isimli kaynak kullanın, eylem adı koymayın: products/{id} yerine getProduct demeyin. Uygulamayı çalıştırıp tarayıcıdan ya da komut satırından bir istek atarak yanıtı görün; ilk çalışan endpoint, ilerisi için güven verir.
Katmanları ayırın: controller, servis, repository
Controller yalnızca HTTP dilini bilir: istek alır, servise iletir, yanıt döndürür. İş kuralları servis katmanındadır; veri erişimi ise repository katmanında. Controller içine iş kuralı koymak, aynı kuralı başka bir yerden çağırmak istediğinizde sorun çıkarır.
Bir diğer ayrım, istek ve yanıt nesneleri ile veri tabanı varlığı (entity) arasındadır. Entity'yi doğrudan API'den döndürmek şu sorunları doğurur: veri tabanı yapınız dış dünyaya sızar ve ilişkili alanlar yanlışlıkla yüklenip serileştirilir. Bunun yerine istek ve yanıt için ayrı sınıflar (ya da Java kayıt sınıfları, record) tanımlayın ve dönüşümü servis katmanında yapın.
Güncelleme ve oluşturma isteği için farklı nesneler kullanmak da yararlıdır: oluştururken kimlik alanı yoktur, güncellerken adres zaten kimliği taşır. Tek bir nesneyi her şey için kullanmak, doğrulama kurallarını karmaşıklaştırır.
HTTP metotları ve durum kodları
Metot seçimi API'nin sözleşmesidir. Okuma için GET, yeni kaynak oluşturma için POST, kaynağı tümüyle değiştirmek için PUT, kısmi güncelleme için PATCH, silme için DELETE kullanın. GET istekleri sunucuda veri değiştirmemelidir.
Durum kodlarında Spring size ResponseEntity ile esneklik verir:
@PostMapping ResponseEntity<ProductResponse> create(@Valid @RequestBody ProductRequest request) { ProductResponse created = service.create(request); URI location = URI.create("/api/products/" + created.id()); return ResponseEntity.created(location).body(created); }
Başarıyla oluşturulan kaynak için 'oluşturuldu' durumu ve Location başlığı döndürmek istemciye yeni kaynağın adresini söyler. Silme işleminde gövdesiz bir 'içerik yok' yanıtı yeterlidir. Bulunamayan kaynak için 'bulunamadı', geçersiz girdi için 'hatalı istek' kullanın. Her şeyi başarılı kodla döndürüp hatayı gövdede anlatmak, istemci kitaplıklarının hata davranışını bozar.
Girdi doğrulama ve tutarlı hata yanıtı
İstek gövdesine doğrulama kuralları ekleyin ve controller'da @Valid ile devreye alın:
record ProductRequest( @NotBlank String name, @Positive BigDecimal price ) {}
Kurala uymayan istek, controller metodu çalışmadan önce reddedilir. Hata biçimini tek bir yerde belirlemek için @RestControllerAdvice sınıfı yazın ve @ExceptionHandler metotlarıyla iş kuralı hatalarını ve doğrulama hatalarını aynı yapıya çevirin. Spring'in desteklediği ProblemDetail yapısı, hata için standart bir gövde sunar: başlık, durum, açıklama ve ek alanlar.
Her hata için istemcinin makine tarafından okuyabileceği bir hata kodu da döndürmek yararlıdır; yalnızca insan okuyacağı bir mesajla yetinirseniz mobil ya da ön yüz ekibi mesaj metnine göre koşul yazmak zorunda kalır. Sunucu iç hatalarında yığın izini yanıta koymayın, kayıt (log) altına alın.
Sayfalama, filtreleme ve test
Liste döndüren her endpoint'te baştan sayfalama düşünün. Spring Data kullanıyorsanız Pageable parametresi, sayfa ve boyut sorgu parametrelerini otomatik bağlar; yanıt olarak tüm tabloyu döndürmek, veri büyüdüğünde sunucuyu yorar. Sıralama ve filtre alanlarını kullanıcıdan gelen serbest metinle doğrudan sorguya yedirmeyin; izin verilen alanları beyaz listeyle sınırlayın.
Test tarafında farklı katmanlar işinize yarar. Controller katmanı için yalnızca web katmanını ayağa kaldıran MockMvc tabanlı testler hızlı çalışır ve istek-yanıt sözleşmesini doğrular. Uçtan uca davranış için ise tam uygulama bağlamını başlatan entegrasyon testleri yazın. Kritik senaryolar şunlardır: geçerli istek, eksik zorunlu alan, bulunamayan kaynak ve yetkisiz erişim. Her hata türü için ayrı bir test, hata yönetimi sınıfındaki gerilemeleri yakalar.
Sık yapılan hatalar
- Entity'yi doğrudan yanıt yapmak. Veri tabanı değişikliği API'yi kırar ve gizli alanlar sızabilir.
- Controller'a iş kuralı yazmak. Kural tekrarlanır, test edilmesi zorlaşır.
- Hata yanıtını her yerde başka biçimde döndürmek. İstemci hata işleme kodunu koşullarla doldurur.
- Doğrulamayı yalnızca ön yüze bırakmak. API, ön yüz olmadan da çağrılabilir.
- Sürüm yönetimini düşünmemek. Yanıt biçimi değişecekse adrese ya da başlığa sürüm bilgisi koymak, mevcut istemcileri korur.
- Lazy ilişkilerin serileştirilmesinde N+1 sorgu sorunu yaşamak. Yanıt nesnesini dönüştürürken yalnızca gereken alanları yükleyin.
- Tüm istisnaları tek bir genel işleyiciyle yutmak. Beklenen ve beklenmeyen hataları ayırın.
Bu beceriyi Mindset programlarında nerede çalışırsınız?
Java Spring Boot ile Backend programı, Java bilen ve backend servisi yazmaya geçmek isteyen yazılımcılar ile mevcut ekibine Spring standartlarını yerleştirmek isteyen kurumsal geliştirme ekipleri için uygun görünüyor. Program başlıkları arasında Spring Web MVC ve REST API, Spring Data JPA ile veri katmanı ve test ile üretim hazırlığı yer alıyor; bu sayfadaki controller, katmanlama ve test konuları o başlıklara denk düşüyor.
API tasarım ilkelerini Spring'den bağımsız olarak, kaynak modelleme, durum kodları ve sayfalama açısından derinleştirmek isteyenler için REST API En İyi Pratikler programındaki modüller tamamlayıcı olabilir. Ekipçe uygulandığında, ortak hata yanıtı biçimi ve katman kuralı kod incelemelerini hızlandırır.
İlgili Mindset programı
Java Spring Boot ile Backend
Sık sorulan sorular
@Controller ile @RestController arasındaki fark nedir?
@Controller, görünüm adı döndüren klasik sayfa tabanlı uygulamalarda kullanılır. @RestController ise @Controller ile yanıt gövdesi anotasyonunu birleştirir; metotların döndürdüğü nesne doğrudan JSON olarak yazılır. Böylece her metoda ayrıca @ResponseBody eklemeniz gerekmez.
DTO kullanmak zorunlu mu?
Zorunlu değil ama önerilir. Entity'yi doğrudan döndürmek veri tabanı yapısını dışa açar ve ilişkili alanların istemeden serileştirilmesine yol açar. İstek ve yanıt için ayrı sınıflar ya da Java kayıt sınıfları kullanmak sözleşmeyi veri modelinden bağımsız tutar.
PUT ile PATCH ne zaman tercih edilir?
PUT kaynağın tamamını yenisiyle değiştirir; gönderilmeyen alanlar boşalır. PATCH yalnızca gönderilen alanları günceller. Örneğin kullanıcının yalnızca telefon alanı değişiyorsa PATCH, kayıt bütünüyle yeniden gönderiliyorsa PUT uygundur. Kısmi değişikliklerde PATCH daha güvenlidir, ancak hangi alanların güncelleneceğini ve boş gelen değerin ne anlama geldiğini ayrıca tanımlamak gerekir.
Spring MVC ile WebFlux arasında nasıl seçim yaparım?
Çoğu klasik CRUD servisi için Spring MVC yeterlidir ve ekip için daha tanıdıktır. Çok sayıda eşzamanlı, bekleyen bağlantıyı az iş parçacığıyla yönetmeniz ya da akış tabanlı çalışmanız gerekiyorsa WebFlux düşünülebilir. Reaktif yapı, kod okunabilirliğini ve hata ayıklamayı zorlaştırır.
API dokümantasyonunu nasıl üretirim?
OpenAPI uyumlu bir kitaplık, controller anotasyonlarından API sözleşmesini üretip tarayıcıda denenebilir bir arayüz sunabilir. Üretilen çıktıyı gözle kontrol edin; hata yanıtları ve doğrulama kuralları otomatik olarak her zaman tam yansımaz.
İlgili rehberler
İlgili eğitimler
Kategori: Yazılım Geliştirme