REST’e rest çekmek

Roy Thomas Fielding diye biri 2000 yılında bir doktora tezi yazdı. Öyle her baba yiğit doktora tezi yazarak dünyayı değiştiremez. Fielding’in de kendi kendinden böyle bir beklentisi olduğunu sanmıyorum. Fakat bugün tezinde tarif ettiği REST kavramının sunucu-istemci mimarili projelerin temel taşlarından biri haline geldiğini görüyoruz.

Sunucu ile istemci arasındaki iletişimi tasarlarken REST tarzını baz almak çok moda. Hatta birçokları için REST demek “doğru” Web API tasarımı demek. Oysaki Fielding’in tasarısı ile yapılan Web API’leri arasında şaşırtıcı derecede büyük farklar var.

HTTP isteklerine cevap vermekle REST olunsaydı, REST diye ayrı bir tanıma gerek olur muydu?
“Ama HTTP Verb’ine dikkat ettim, Header’ları doğru yolluyorum” demek geldiyse içinizden: evet, işte onun adı zaten HTTP.

REST, farklı protokol tasarımı ihtiyaçlarını kendi içinde eriten, tüm diğer sunucu-istemci protokollerini bünyesine alacak bir jenerik protokol olarak düşünülmüş. Bu jenerik protokol sayesinde istemci ile sunucunun tasarımı birbirinden bağımsız olacaktı. Sunucuyu gerçekleyenin istemciden, istemciyi gerçekleyenin sunucudan haberdar olması lazım değildi.

Web özelinde, tarayıcının bazı içerik türleri için (HTML, CSS, JPEG, PNG gibi) jenerik istemci olmayı başardığını görüyoruz. Benzer şekilde web sunucuları da bazı içerik türleri için jenerik sunucu olmakta başarılıdır.

HTTP standardına “Content-Type” ve “Accept” gibi başlıkların eklenmesinin sebebi, istemci ile sunucunun içeriğin formatı konusunda anlaşabilmesi, böylece istemcinin içeriği doğru bir şekilde yorumlamak için önkabullerde bulunması gerekmemesiydi. Burada önkabulden kasıt belirli bir adres, sunucu veya bir parametre için istemcinin “şundan gelen cevaba böyle işlem yapılacak” diye sabit bir mantık kurmaktır.

İçerik hakkında önkabullerden arınmanın yolu “Content-Type”ın içerikten nasıl faydalanacağımızı mükemmel olarak tarif etmesidir. Böylece jenerik istemci o içerik türüne özel modüllerini işletebilir, veya en kötü ihtimalle bu içerik türünü desteklemediği yönünde kullanıcıyı uyarabilir.

En son ne zaman kendi Content-Type etiketinizi yarattınız? Hiç mi? Web API’nizin cevapları ile ne yapacağımızı nereden bileceğiz?
REST’in EN TEMEL tezine göre “isteği gönderen bilir ne yapacağını!” bir cevap değil.

“Content-Type”ın içerikten nasıl faydalanacağımızı birebir tanımlamasının bir yolu da her içeriğin (Resource) bir bağımsız bütün olması, kendi başına anlamlı olmasıdır. Her ne kadar Fielding tezinde REST içeriğinin birebir dosya kavramına denk gelmediğini iddia etse de içeriği dosya olarak tasavvur etmekten kaçamamış. Çünkü istemcinin jenerik olabilmesi için içerikler arasındaki ilişkinin URI’ler ile kurulması, bir içerikteki değişikliğin diğer içerikleri etkilememesi, içeriğin parçalanamaz bir bütün olması ve kendi başına anlamlı olması gerekir.

Bugün biz Web API’leri tasarlayan insanlar “Content-Type” olarak “application/json” demekten öteye gitmiyoruz. “application/json” içeriğin nasıl kullanılacağı hakkında en ufak bilgi vermeyen bir MIME Type’dır. Bu bağlamda kendi başına bir MIME Type olmayı nasıl hak ettiği de sorgulanabilir. Bir istemci sunucudan aldığı içeriği nasıl işleyeceğine “application/json” etiketine bakarak karar veremez. Sunucu bağlamından haberdar olması, sunucunun vereceği cevabın gerçek formatını bir önkabul olarak bilmesi gerekir.

REST’in daha en temel tezinde piyasadaki tüm REST API’leri kaybettik. Kalan sağlar bizimdir dahi diyemiyoruz.

İstisnalar hariç Web API’leri veriyi ilişkisel bir veritabanında tutuyor. Bir nesneyi REST’e uygun olarak dönüştürüp istemciye yolladığımızda diğer içeriklere referans vermek için veritabanındaki ID’lerini koyuyoruz. İlgili nesnelerin URI’lerini oluşturup cevabın içine koymak gibi bir huyumuz yok. Bundan ötürü tekil bir içeriği istemciye doğru tarif etmediğimiz gibi, o içerikle ilgili olan başka içerikleri de sunucu tasarımı hakkında önkabuller olmadan işaret edemiyoruz.

Oysaki REST’in bir diğer varsayımı da hypermedia’dır. Yani içeriğin ilgili diğer içerikleri standart bir biçimde (URI) ifade etmesi, böylece jenerik istemcinin ilgili içerikleri dolaşabilmesidir. Şu anda kadar standartlaşmış olarak diğer içerikleri URI ile işaret edebilen tek içerik tipi HTML’dir. Dolayısı ile bizim JSON biçmindeki cevaplarımızın jenerik bir istemciyi ilişkili içerikler arasında gezdirmesi olası değildir. (Not: bu konuda standartlar yaratma çabaları olduğundan haberim var, ama hiçbiri geniş kabul görmüş değil)

Bir deli kuyuya taş atmış, kırk akıllı çıkaramamış.

REST’in bir parçası olan URI standardı klasik dosya sistemlerinden etkilenmiştir. Dosya sistemleri veriyi bir ağaç yapısında tutar. Bildiğiniz gibi dosya sistemindeki yol verinin ağaç hiyeraşisi içindeki yerini belirtir. URI standardı dosya yolu kavramını birebir devralmıştır. İçeriğin hiyeraşik olarak sınıflandırması içeriğin yapısı hakkında sorgulamadığımız bir kabuldür.

URI standardını benimseyen REST’e bakan göz “REST” olmak için evrendeki tüm veriyi bir ağaç olarak modellemek gerektiğini, yaptığı projenin bu büyük ağacın bir alt ağacı olduğunu, tüm verisini bir ağaç gibi dizmesi gerektiğini zanneder. Kavramsal olarak kulağa çok güzel gelse de modellediğiniz verinin gerçekten bir ağaç düzeninde olma olasılığı pek düşüktür.

“REST” API’leri yazarken şekilsel benzerliği korumak için URI standardına uymaya ve verimizi dış dünyaya bir hiyeraşi içinde açmaya çalışıyoruz. Oysa ilişkisel veritabanında içeriğimiz bir ağaç değil bir ağ yapısı halinde duruyor. Verimizi modellerken hemen hemen hiç bir zaman tepeden aşağı tüm veriyi kapsayan bir hiyeraşi çizmiyoruz. Bizim için verinin nihayi işaretçisi tablonun adı ve ID değerinden ibaret oluyor.

Zaten ideal zannedilen alışılmış REST adresleme alışkanlığı da bu çelişkiyi göz önüne seriyor:

GET http://sitem.com/konu-basliklari/1231/alt-gruplar/4121/detay

Bu örnekte “alt-gruplar”ın “konu-basliklari/1231”nın bir alt elemanı olduğunu ifade ediyoruz. Ama sen, ben, o, herkes biliyor ki bu ikisi bağımsız tablolar ve bir ast-üst ilişkisi içinde değiller. Bu yalanı sadece daha “REST” olduğumuz zannedilsin diye sürdürüyoruz.

İşin garibi Fielding’in tezinde verinin hiyeraşik olarak modellenmesine hiç vurgu yok. URI’lerin bir ağaç yapısı tanımlaması REST açısından tarihsel bir mirastan ibaret. Kısacası bu kadar kasmasak da olur muydu, olurdu.

İstemci ile sunucunun birbirini tam olarak tanıdığı, tamamen önkabullerle işleyen, içerikleri URI’ler ile birbirine bağlamayan, verisi aslında hiyeraşik olmayan, döndüğü içeriğin tipini tanımlamayan “REST” API’leri yazıyoruz.

REST çıkmadan öncede böyle işler yapılıyordu. O zaman adına “RPC” yani “Remote Procedure Calling” deniyordu. Özünde yaptığınız şey sunucudaki bir prosedürü (keyfinize göre metot veya fonksiyon da diyebilirsiniz) çağırmaktan ibaret. Prosedürünü adını, parametrelerini, dönüş tipini bilmek hep istemciyi yazanın görevi. Tıpkı bizim yazdığımız sözde “REST” API’leri gibi.

Mesela eğer yukarıdaki istek örneğini RPC olarak yazsak şöyle olabilirdi:

GET http://sitem.com/konu-basligi-alt-gruplar-detay?kb_id=1231&ag_id=4121

“REST” diye ne yapmışız? parametreleri sondan alıp prosedür adının içine yazmışız. Bunu derken aklıma Objective-C geldi. Bu dilde sunucu kodu hiç yazılmamış olması çok üzücü. Eğer yazılmış olsaydı bizim “REST” çağrısının RPC karşılığı sunucuda şöyle görünecekti:

[sitem getKonuBasligi: 1231 altGrup: 4121 detay];

Kısacası parametreleri araya sıkıştırmak sentaksa dair bir detaymış. Semantik olarak hiç bir fark yaratmamışız. Yaptığımız iş düpedüz RPC. Hadi adını koyalım.

Burada çoğunluğun aksine REST iyi RPC kötü demeyeceğim. REST’i ulaşılması gereken bir ideolojik hedef haline getirmenin de anlamı yok. Bizim ihtiyacımız olan şey RPC, yaptığımız şey RPC, boşu boşuna kendimizi kandırmayalım diyorum.

Hatta Fielding’in tezinin büyük ölçüde hayal olduğunu, “jenerik istemci”nin imkansız olduğunu da söylemek mümkün. Hedefe en yakın istemciler olan web tarayıcıları bile bu görevi dar bir içerik tipi kümesi için yerine getirebiliyor.

Bütün bunlar ışığında, alışıldık RESTimsi ezberi sürdürmeyi anlamsız buluyor, API tasarlarken RPC yaptığımı kabullenerek hareket ediyorum. Yazılım dünyası bu yaklaşımı zamanla benimser mi bilemem, ama ben yaptığım şeye zorla REST etiketi yapıştırmıyorum.

REST üzerine daha detaylı bir tartışma sürdürmek isteyen olursa turerkan@houseofapps.com adresinden bana ulaşabilirler.

Okumaya küsmek isterseniz buyrun Fielding’in orjinal tezine: https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm