API’nız, dünyaya sırlarınızı açıyor olabilir
API’nız, dünyaya sırlarınızı açıyor olabilir
Endpoint’inizi oluşturuyorsunuz, Insomnia’da test ediyorsunuz, JSON güzel bir şekilde dönüyor ve işinize devam ediyorsunuz. Deploy işlemi tamam, her şey yolunda.
Fakat bu JSON içinde, siz yalnızca name ve email alanlarını göstermek isterken, password (hash’li bile olsa), remember_token, stripe_id ve sadece ekibin görmesi gereken internal_notes gibi alanlar da yer alıyor.
Kimse bunu fark etmedi, ta ki birisi fark edene dek. 😬
Bu durum, Model’inizi doğrudan döndürmenin bedeli. Laravel’deki çözüm: API Resources.
Herkesin yazdığı kod
Herkesin yazdığı kod
Bu controller’ı inceleyin. Bunun gibi bir şey yaptığınızı tahmin ediyorum:
public function show(User $user)
{
return response()->json($user);
}
// ya da "pratik" versiyonu:
public function index()
{
return User::all();
}
Bu yöntem çalışıyor ve kullanıcıyı döndürüyor. Ancak return $user dediğinizde, Laravel toArray() metodunu çağırır ve bütün sütunları JSON olarak döker.
Tüm sütunlar. Hem sizin istediğiniz, hem de unuttuğunuz. Eğer yarın birisi yeni bir documento ya da salario alanı eklerse, bu, siz istemeden API’nizde açığa çıkabilir.
Yanıt formatı üzerinde hiç kontrolünüz yok. API bir sözleşmedir — veritabanının çiğ halini buraya bırakmak, bu sözleşmenin tam tersidir.
$hidden yardımcı olur, ama çözüm değil
$hidden yardımcı olur, ama çözüm değil“Ama ben Model’de $hidden kullanıyorum.” Harika, ama yeterli değil:
protected $hidden = [, ];
Problemi, $hidden bir blacklist olarak çalıştığı için, her yeni hassas alanı gizlemeyi unutmamanız gerekiyor. Unuttunuz mu? Bilgiler açığa çıktı!
Bir diğer mesele ise, aynı Model farklı yerlerde kullanılabiliyor. Bazen yanıt içinde email görmek istersiniz, bazen istemezsiniz. $hidden bu kararı her endpoint için değil, genel bir yapı olarak kontrol eder.
Çözüm: Yanıtı oluşturacak bir katman
Çözüm: Yanıtı oluşturacak bir katman
API Resource, Model’iniz ile JSON arasında bulunan bir sınıftır. Her alan için, çıktıda ne olacağına karar verir. Bu bir whitelisttir: sadece buraya yazdıklarınız çıkar.
Aşağıdaki komut ile oluşturabilirsiniz:
php artisan make:resource UserResource
Ve formatı toArray() metodunda belirleyebilirsiniz:
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
=> $this->id,
=> $this->name,
=> $this->email,
=> $this->created_at->format(),
];
}
}
Bu yapı ile, listedeki herhangi bir alan yer almadığında JSON’da görünmeyecek. password asla açığa çıkmayacak çünkü onu sadece yazmadınız. Bu yapı varsayılan olarak güvenlidir, sizin hatırlamanıza bağlı değil.
Ayrıca formatı kontrol etme imkânınız var: name alanını nome olarak adlandırabilir, tarihi formatlayabilirsiniz. Yanıt sizin kontrolünüzde, doğrudan veritabanının bir yansıması değil.
Controller içinde nasıl kullanılır
Controller içinde nasıl kullanılır
Controller’da Model’inizi Resource içinde sarmalayın:
public function show(User $user)
{
return new UserResource($user);
}
Bir liste için ise collection() metodunu kullanabilirsiniz:
public function index()
{
return UserResource::collection(User::all());
}
Tek Resource ile, ister bir kayıt ister bin tane kayıt olsun. Laravel, her bir öğeyi dönüştürme işlemini otomatik olarak gerçekleştirir.
Varsayılan olarak, yanıt data anahtarında gelir — bu, meta veriler (sayfalandırma, bağlantılar) için yer sağlar ve sözleşmeyi bozmadan ilerleyen süreçte esneklik sunar.
Özel durum: Koşullu alanlar
Özel durum: Koşullu alanlar
Burada Resource, $hidden ile farkını ortaya koyuyor. Bazı alanların yalnızca belirli kişilere görünmesi gerekebilir. Örneğin, adminin göreceği, normal bir kullanıcının görmeyeceği bilgiler.
public function toArray(Request $request): array
{
return [
=> $this->id,
=> $this->name,
=> $this->email,
// sadece istek yapan kişi admin ise JSON'a girecek
=> $this->when(
$request->user()?->isAdmin(),
$this->internal_notes
),
];
}
when() yalnızca şart doğruysa anahtarı JSON’a ekler. Normal bir kullanıcı için, alan yanıt içinde hiç bulunmaz — boş ya da null olarak değil, tamamen yok. Açığa çıkması imkânsız.
İlişkiler için, whenLoaded() metodu sayesinde ekstra sorguları da önlemiş olursunuz:
=> PostResource::collection($this->whenLoaded()),
İlişki yalnızca with('posts') ile yüklemişseniz JSON’a eklenir. Yüklemediyseniz? Gösterilmez — Laravel ayrıca sorgu göndermez. Güvenlik ve performans birlikte bu yöntemde sunulmuş olur.
Sıklıkla karıştırılan bir tuzak
Sıklıkla karıştırılan bir tuzak
Resource içinde, alanlara $this->id, $this->name gibi erişmek, büyülü bir durum gibi görünebilir; çünkü $this Model değil, Resource’dır.
Gerçek şu ki, Resource erişimi Model’e yönlendirir. Dolayısıyla $this->name pratikte $user->name demektir. Harika çalışıyor.
Ancak bir tuzak, Model’in herhangi bir metodunu $this üzerinden yüklenmeden çağırabileceğinizi zannetmektir; ya da bir koleksiyonda, $this‘in her bir öğeyi temsil ettiğini unutmak — koleksiyonun tamamını temsil etmez. Eğer koleksiyonu tek bir nesneymiş gibi işlemeye çalışırsanız, sorun yaşarsınız.
Ek: Peki şimdi ne?
Ek: Peki şimdi ne?
Eğer bir Servis Katmanınız varsa, controller’ınız sade olacaktır: Servis iş kurallarını çözer, Resource ise çıktıyı formatlar. Her bir yapı kendi alanında çalışır.
Ayrıca make:resource --collection komutuna ve additional() metoduna göz atmayı unutmayın; bunlar da toArray() girişini kirletmeden yanıtınıza ek veriler koymanıza olanak tanır.
Sekmeyi kapatmadan önce
Sekmeyi kapatmadan önce
Şimdi hızlı bir test yapın: Projenizdeki en eski endpoint’i alın, o doğrudan Model döndüren olanı, ve dönen JSON’u kontrol edin. O JSON içinde yer alması gereken ancak bulunmaması gereken herhangi bir alan var mı?
Eğer varsa, bir refaktör için yeni bir fırsat keşfetmiş oldunuz. Eğer yoksa, büyük olasılıkla şanslısınız — kontrol altında değil, bu sadece tesadüf.
Her durumda, Resource kullanıyor musunuz? Yoksa sadece yanıt karmaşık olduğunda mı? Bunu nasıl düzenlediğinizi yorumlarda paylaşın.
Bu yazı size şüpheli bir endpoint’i hatırlattı mı? return $model ile düşünen bir arkadaşınıza paylaşmayı unutmayın.
Kaynak: Orijinal Makale


