
PHP ile REST API Yazmak: Yönlendirme ve HTTP Durum Kodları
Bir API, arayüzden farklı olarak insan tarafından değil başka bir programla tüketilir. Tüketen taraf tahmin yürütemez: adreslerin öngörülebilir, durum kodlarının doğru ve hata biçiminin tutarlı olması gerekir. Bu bölümde saf PHP ile küçük bir API'nin nasıl kurulacağı ele alınıyor.
Amaç bir çatı yazmak değil, hazır çatıların hangi sorunları çözdüğünü anlamaktır. Aynı ilkeler, kullanılan araç ne olursa olsun geçerlidir.
Kaynak adlandırma
Adresler eylemi değil kaynağı gösterir; eylem HTTP yöntemiyle belirtilir. Kaynak adları çoğul yazılır ve iç içe geçmiş ilişkiler adres yapısında gösterilir.
GET /api/v1/yazilar: listelemeGET /api/v1/yazilar/42: tek kayıtPOST /api/v1/yazilar: oluşturmaPATCH /api/v1/yazilar/42: kısmi güncellemeDELETE /api/v1/yazilar/42: silmeGET /api/v1/yazilar/42/yorumlar: alt kaynak
Adreslerde eylem adı kullanılması, aynı işi yapan birden çok uç oluşmasına yol açar. Kaynak odaklı düzende ise yeni bir işlem eklendiğinde adres yapısı değişmez; yalnızca yeni bir yöntem desteklenir. Bu tutarlılık, tüketen tarafın belgelere daha az bakmasını sağlar.
Sürüm numarasının adreste bulunması, ileride uyumluluğu bozan bir değişiklik yapılması gerektiğinde eski tüketicilerin çalışmaya devam etmesini sağlar.
Basit bir yönlendirici
Yönlendirici, yöntem ve yol ikilisini bir işleyiciye eşler. Yoldaki değişken bölümler desene çevrilir ve yakalanan değerler işleyiciye aktarılır.
final class Yonlendirici
{
/** @var list<array{yontem:string, desen:string, isleyici:callable}> */
private array $rotalar = [];
public function ekle(string $yontem, string $yol, callable $isleyici): void
{
$desen = '#^' . preg_replace('#\{(\w+)\}#', '(?<$1>[^/]+)', $yol) . '$#';
$this->rotalar[] = ['yontem' => $yontem, 'desen' => $desen, 'isleyici' => $isleyici];
}
public function calistir(string $yontem, string $yol): mixed
{
foreach ($this->rotalar as $rota) {
if ($rota['yontem'] !== $yontem) {
continue;
}
if (preg_match($rota['desen'], $yol, $p) === 1) {
$parametre = array_filter($p, static fn ($k) => !is_int($k), ARRAY_FILTER_USE_KEY);
return ($rota['isleyici'])($parametre);
}
}
throw new BulunamadiHatasi();
}
}$yol = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$yontem = $_SERVER['REQUEST_METHOD'];
$router = new Yonlendirici();
$router->ekle('GET', '/api/v1/yazilar', $listele);
$router->ekle('GET', '/api/v1/yazilar/{id}', $getir);
try {
$router->calistir($yontem, rtrim($yol, '/'));
} catch (BulunamadiHatasi) {
jsonYanit(['hata' => 'Kaynak bulunamadı'], 404);
}Durum kodları
Durum kodu, yanıtın gövdesi okunmadan önce sonucu anlatır. Yanlış kod kullanmak, tüketen tarafta hatalı davranışa yol açar: başarısız bir işlem 200 koduyla dönerse istemci hata denetimi yapmaz.
200: başarılı okuma ya da güncelleme201: kayıt oluşturuldu,Locationbaşlığı eklenir204: başarılı, gövde yok (silme işlemleri)400: istek biçimi hatalı401: kimlik doğrulanmadı,403: yetki yok404: kaynak yok,409: çakışma422: biçim doğru ancak doğrulama başarısız429: hız sınırı aşıldı,500: sunucu hatası
Kimlik doğrulanmadı ile yetki yok ayrımı sık karıştırılır. Birincisi kimliğin hiç ya da geçersiz sunulduğunu, ikincisi kimliğin geçerli olduğunu ancak bu işlem için yeterli olmadığını anlatır.
Kimlik doğrulama
API isteklerinde oturum çerezi yerine yetkilendirme başlığı kullanılır. Belirteç sunucu tarafında saklanır ve karşılaştırma sabit zamanlı yapılır.
$baslik = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (!str_starts_with($baslik, 'Bearer ')) {
jsonYanit(['hata' => 'Yetkilendirme başlığı eksik'], 401);
}
$belirtec = substr($baslik, 7);
if (!hash_equals($beklenen, $belirtec)) {
jsonYanit(['hata' => 'Geçersiz belirteç'], 401);
}Belirteçler veritabanında düz olarak saklanmaz; özetleri tutulur ve karşılaştırma özet üzerinden yapılır. Böylece veritabanı kopyası ele geçse bile belirteçler doğrudan kullanılamaz. Her belirtecin bir son kullanma tarihi ve iptal edilebilme yolu bulunmalıdır.
Bazı sunucu yapılandırmalarında yetkilendirme başlığı PHP'ye aktarılmaz. Bu durumda sunucu tarafında ilgili yönlendirme kuralının eklenmesi gerekir; aksi halde doğru gönderilen istekler de reddedilir.
Doğrulama ve hata biçimi
Hataların tek bir biçimde dönmesi, tüketen tarafın tek bir çözümleme kodu yazmasını sağlar. Alan bazlı doğrulama hataları ayrı ayrı listelenir.
$hatalar = [];
if (!is_string($veri['baslik'] ?? null) || mb_strlen(trim($veri['baslik'])) < 3) {
$hatalar['baslik'] = 'En az üç karakter olmalı';
}
if (!in_array($veri['durum'] ?? '', ['taslak', 'yayinda'], strict: true)) {
$hatalar['durum'] = 'Geçersiz değer';
}
if ($hatalar !== []) {
jsonYanit(['hata' => 'Doğrulama başarısız', 'alanlar' => $hatalar], 422);
}Listeleme, sayfalama ve süzme
Liste uçlarında üst sınır konulmaması, tek bir isteğin tüm tabloyu belleğe almasına yol açar. Sayfa boyutu istemciden alınır, ancak sunucu tarafında sınırlanır.
$adet = min(max((int) ($_GET['adet'] ?? 20), 1), 100);
$sayfa = max((int) ($_GET['sayfa'] ?? 1), 1);
$atla = ($sayfa - 1) * $adet;
jsonYanit([
'veri' => $kayitlar,
'sayfalama' => [
'sayfa' => $sayfa,
'adet' => $adet,
'toplam' => $toplam,
],
]);Süzme ve sıralama ölçütleri de sunucu tarafında sınırlanır. İstemcinin belirlediği sütun adının doğrudan sorguya girmesi, hem enjeksiyon riski taşır hem de indekslenmemiş sütunlarda ağır sorgular üretir. İzin verilen ölçütler listelenir ve dışındakiler reddedilir.
Çok büyük tablolarda sayfa numarasına dayalı atlama, ilerleyen sayfalarda yavaşlar. Sıralama ölçütüne göre son kaydın anahtarını taşıyan imleç tabanlı sayfalama bu durumda daha uygundur.
Önbellek ve koşullu istekler
Sık değişmeyen kaynaklarda, aynı veriyi tekrar tekrar göndermek yerine istemcinin elindeki kopyanın hala geçerli olduğunu bildirmek daha ucuzdur. Yanıtla birlikte gönderilen etiket, sonraki istekte geri gönderilir ve içerik değişmemişse gövdesiz bir yanıt döner.
$etiket = '"' . md5($icerikOzeti) . '"';
header('ETag: ' . $etiket);
header('Cache-Control: private, max-age=60');
if (($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') === $etiket) {
http_response_code(304); // gövde gönderilmez
exit;
}
jsonYanit($veri);Hız sınırlaması da API'nin ayrılmaz parçasıdır. Sınıra ulaşan istemciye 429 kodu ile birlikte ne kadar beklemesi gerektiği bildirilir; bu bilgi olmadan istemciler genellikle hemen yeniden dener ve yük daha da artar.
header('X-RateLimit-Limit: 100');
header('X-RateLimit-Remaining: ' . $kalan);
if ($kalan <= 0) {
header('Retry-After: 60');
jsonYanit(['hata' => 'Hız sınırı aşıldı'], 429);
}Kontrol listesi
- Adresler kaynak adı taşıyor, eylem HTTP yöntemiyle belirtiliyor.
- Sürüm numarası adres yapısında yer alıyor.
- Her yanıtta doğru durum kodu ve
Content-Typebaşlığı var. - Hata gövdeleri tek biçimde; doğrulama hataları alan bazlı dönüyor.
- Kimlik doğrulama başlıkla yapılıyor, karşılaştırma sabit zamanlı.
- Liste uçlarında sayfa boyutu sunucu tarafında sınırlanıyor.
- Sunucu hataları istemciye ayrıntı sızdırmıyor, günlüğe yazılıyor.
İlgili yazılar
PHP eğitim serisi
Bu yazı, temel sözdiziminden üretim ortamına kadar ilerleyen 25 bölümlük serinin bir parçası. Bölümler sırayla okunacak biçimde hazırlandı, ancak her biri tek başına da kullanılabilir.
Temeller
- Kurulum ve temel sözdizimi
- Döngüler ve akış kontrolü
- Diziler
- Fonksiyonlar
- String fonksiyonları
- Süper globaller
- Form işleme
- Dosya işlemleri
Dilin ayrıntıları
Nesne yönelimli PHP
- Sınıf ve nesne
- Kalıtım, arayüz ve trait
- Statik üyeler, sabitler ve enum
- Namespace, Composer ve autoload
Veri ve güvenlik
- PDO ile veritabanı işlemleri
- Oturum yönetimi ve kimlik doğrulama
- Güvenli çıktı ve XSS
- JSON ile veri değişimi
Dış dünya ve üretim
- cURL ile dış API çağırma
- Kendi REST API'ni yazmak (bu yazı)
- PHPUnit ile birim test
- Hata ayıklama ve performans ölçümü
- Üretime alma ve güvenlik
Yorumlar
İlk yorumu sen yap.
İlgili Yazılar

PHP Uygulamasını Üretime Almak: Yapılandırma ve Güvenlik
Geliştirme makinesinde çalışan kod ile üretimde güvenle çalışan kod arasındaki fark yapılandırmadır. Ayarlar, izinler, dağıtım akışı ve son kontroller.

PHPUnit ile Birim Test Yazmak: Kurulum, Sahte Nesneler ve Kapsam
Testin amacı hata bulmak değil, değişikliği güvenle yapabilmektir. Kurulum, ilk test, sahte nesneler ve test edilebilir kodun özellikleri.

PHP'de Hata Ayıklama, Günlükleme ve Performans Ölçümü
Ölçmeden yapılan iyileştirme tahmindir. Hata ayıklayıcı kurulumu, yapılandırılmış günlükler ve darboğazı gerçekten bulan ölçüm yöntemleri.