Delphi de bir Rest Api geliştirmeye çalışıyorum.
Ancak epey araştırmama rağmen kafamda oturmayan bazı hususlar var.
Sorularım ve kafama oturmayan hususlar RestClient ile alakalı değil.
İşin SERVER tarafıyla alakalı.
İnternette çok fazla türkçe kaynak hatta ingilizce bile çok yüzeysel.
İspanyolca ve Portekizce kaynaklar ise epey bir fazla.
Sanırım artık Delphi sadece İspanyollar kullanıyor.
Bu defa kervan yolda düzülsün istemiyorum.
Proje olgunlaştığında refactoring yapmak istemiyorum.
İllaki olacaktır ama mimari yapı değişikliği gerektiren refactoringler ile uğraşmak istemiyorum.
Daha yolun başındayım ve o yüzden de doğru yönde ilerlemek istiyorum.
Öncelikle .Net ortamında C# ile Rest Api yapabilirdim.Bu konuda yeterince bilgim var.Ancak bir kaç sebep ile bundan vazgeçtim.
1.C# ta bu işi yapınca mecburen Entity Framework kullanacağız.Çünkü alışmışız.
Direkt Db CRUD ları yapmayacağız.Ama Delphi tarafında ise hazır classlarım kütüphanelerim var.
Entity Framework kadar bana kolaylık sağlıyorlar ve direkt SQL ile çalıştıklarından daha performanslılar.Ayrıca ilk göz ağrımız Delphi ile daha rahat olacağımı düşünüyorum.
2.Delphi' de Serveri ISAPI.dll ile oluşturabilmenin yanında birde Exe olarak oluşturabiliyoruz ki (Kurulum,Ayar,Dağıtım v.s. için süper kolaylık) tadından yenmeyecek bir özellik.
Ama C# ile yaptığımızda (en azından ben bilmiyorum) böyle bir şansımız yok.
Bu sebeplerden dolayı Delphi ile bu işi yapmaya karar verdim.
Öğrenmek istediğim hususlar şunlar.
Model 1)Sihirbaz ile oluşturduğumuz bir Datasnap Rest Server Uygulamasında TServerMethods1 diye bir class oluşturuyor.2 adet te demo function oluşturuyor.Reversestring ve EchoString şeklinde.
Biz bu servere client üzerinden datasnap bileşenleri ile istek atacağımız zaman bu fonksiyonlara bir parametre gönderiyoruz.
Örneğin TServerMethods1 classı üzerinde GetCari diye bir methodumuz olsun.Biz bu methoda şu şekilde bir örnek URI ile istekte bulunuyoruz.
Kod: Tümünü seç
http://localhost:8080/datasnap/rest/GetCari/stringparametremiz
Server tarafında da biz bu fonksiyonda bu parametreyi yakalayıp, bir JsonObject nesnesine deserialize edip,
(Bu JsonObject nesnesinde Token olabilir, cari listesinin hangi kayıtları içereceğini sorgulamamız için gerekli olan Where bildirimleri olabilir, veya clientten istek atarken göndermesini isteyeceğimiz
herhangi bir bilgi olabilir.........
Sonuç olarak serverin istediği tüm bilgileri içeren bir JsonObject serialize edip string olarak servere gönderilir.Ve serverde bu stringi deserialize eder ve bu bilgilere göre yapacağı işleri yapar ve
geriye bir string sonuç döner.Örneğin bir JsonArray objesini string olarak döner.
Clientte bu JsonArray içeren stringi deserialize eder ve kullanıyıcıya gösterir veya ne yapacaksa yapar.
Update,Insert ve Delete islemlerinde de server boolean sonuç döner clientte boolean sonuca göre bu işlemlerin başarılı olup olmadığını anlar.(Tabi burda farklı dönüşlerde yapılabilir.Sadece örnek bir yaklaşım olsun diye yazıyorum.)
Burda bu modelde, bu yaklaşımda kafama takılan hususlar ve çıkardığım sonuçlar şunlar.Bunlar doğru mudur?Yoksa yanlışmı düşünüyorum.
a)Clientten gelen isteğin klasik Rest isteklerinde olduğu gibi gelen istek GET mi? PUT mu? DELETE mi? POST mu?
Bunu bilemiyoruz.Bilme şansımız yok.
Sadece GET harici atılan istekler de atılan istek durumuna göre metod isminde update, delete gibi prefixler arıyor.
Örneğin Cari isminde bir listelenen metodumuz var.
Buna
Kod: Tümünü seç
http://localhost:8080/datasnap/rest/Cari/stringparametremiz
Ama bu URI ye POST istek attığımızda TServerMethods1 classı içinde updateCari şeklinde bir metod arıyor .
Ama bu URI ye DELETE istek attığımızda TServerMethods1 classı içinde cancelCari şeklinde bir metod arıyor .
Ama bu URI ye PUT istek attığımızda TServerMethods1 classı içinde acceptCari şeklinde bir metod arıyor .
Yani epey bir karışık geldi.
Burda şöyle bir yaklaşım olabilir belki.Tabi standartlara ne kadar uyar bilmem.
Hep tek bir uriye POST istek atılır.Gönderilen isteğin içerdiği parametre içerisinde CRUD işlemlerinden hangisinin yapılacağını belirten bir enum,const v.b. bir belirteç gönderilir.O bilgiye göre CRUD işlemlerinden hangisini yapacağını server bilir.
Tek fonksiyonla bu iş bitirilir.
b)Bütün bir projenin kodlarını tek bir Class üzerine yazmamız gerekiyor.TServerMethods1 classı.(Tabii adını değiştirebiliriz ancak bundan Cari için ayrı, Depo için ayrı, Fatura İçin ayrı yapamıyoruz)
En azından ben bu şekilde anladım.
c)En önemlisi ise bu fonksiyonlara parametre gönderirken gönderdiğimiz parametreler encode etsek bile url üzerinden gitmesi gerekiyor.
Oysaki bir clientlerden istek atarken bir Request nesnesi kullanıyoruz.
Bu requestşn bri Header bölümü, bir Body si oluyor. Tabi bazı parametrelerei QueryString olarak ta gönderiyoruz ama Header ve Body seçeneklerimiz var.
---Burda öğrenmek istediğim şey, diyelim ki bu modelleme ile Apimizi geliştirmeye başladık.İleride URI üzerinden gidecek veride bir boyut sınırlamasına takılma riskimiz varmıdır?
---Bu modelde Request nesnesi gönderme şansımız varmıdır?Benim anladığım kadarı ile yoktur.
Bütün bunlardan çıkardığım sonuç şudur.Doğru mu düşünüyorum sizce?
Bu model, Rest Apiye eğer sadece delphi ile geliştireceğimiz bir RestClient projesinden ulaşım sağlamak istiyorsak tercih edeceğimiz bir model olmalı.
Yani bu Api ye bir web uygulamasından, HTTP üzerinden bildiğimiz klasik GET,PUT,DELETE istekleri atmak istiyorsak tecih edeceğimiz bir model olmamalı.
Ama yine de buna mecbur kalırsak eğer bir web uygulamasından yada Delphi dışında bir dil ile geliştirreceğimz bir mobil uygulamadan atacağımız isteklere aşağıdaki gibi olacaktır.
GET için:
Kod: Tümünü seç
http://localhost:8080/datasnap/rest/updateCari/stringparametremiz
Kod: Tümünü seç
http://localhost:8080/datasnap/rest/updateCari/stringparametremiz
Kod: Tümünü seç
http://localhost:8080/datasnap/rest/cancelCari/stringparametremiz
Kod: Tümünü seç
http://localhost:8080/datasnap/rest/acceptCari/stringparametremiz
Ama bu durumda da servere göndermek istedğimiz tüm veri "stringparametremiz" şeklinde url üzerinden gönderebiliriz.
Her ne kadar isteklerimizin methodlarını istek atarken POST -PUT-DELETE-GET gibi belirtsek bile pek bir esprisi olmaz sonuda yine isteğin tipi ne olursa olsun URI de metodun ismini belirtmemiz gerekir
ve sanki normal bir VCL uygulamada yaptığımız gibi salında bir metod çağırmış oluruz.Request kullanamayız.HTTP mimarisini kullanmamış oluruz.
Bir fonksiyon çağırmış oluruz ve Response ile de bu fonksiyondan dönen değeri yakalayıp işleriz.
Yani sonuç olarak eğer RestClient projemizi delphide geliştirmeyeceksek, veya delphide geliştireceksek bile Datasnap kütüphanesini implemente etmeyen bir client uygulama geliştirmeyecek isek
RestServeri bu modalde geliştirmenin hiç bir avantajı olmadığı gibi bir çok sakıncasıda var.Sizce doğru mudur?
Yada bir başka deyişle aslında Datasnap Rest Server HTTP üzerinden veri alışverişi gerçekleştirmek için HTTP mimarisini kendisi implemente etmiş ve geliştiriciyi HTTP nin detaylarından kurtararak işi SADECE bir metoda istek atıp,
dönen sonucu da işlemek olarak ifade edilebilecek bir sadeliğe indirmiş bir kütüphanedir.Sizce doğrumudur?
Model 2)
Bu modelde ise bir TWebModule nesnesi üzerinde actionlar tanımlayabiliyoruz.Ve bu actionların Request ve Response nesnelerini parametre olarak alıyor.Ve clientlerden gelen Request nesnesini burda yakalıyoruz ve
Header'inde,Body'sinde veye URL parametrestinde ki değerleri yakalayıp, gerekli işlemi yapıp, bir Response nesnesi dönebiliyoruz.
Sanırım bu modeli kullanırsak eğer TServerMethods1 classına ihtiyacımız olmuyor.
Bütün herşeyi bu WebModule nesnesi üzerinde actionlar tanımlıyoruz.
Ve bu actionların gerçekleşme anında da gelen Request nesnesinden isteğin türünün GET-POST-PUT-DELETE olduüunu çözümleyip,
GET ise GetCari methodunu,
DELETE ise DeleteCari methodunu,
PUT ise UpdateCari methodunu,
POST ise InsertCari methodunu çağırıp gerekli operasyonları yaptıktan sonra da
Response nesnesi ile cliente istedğimiz veriyi gönderiyoruz.(GET metodunda bir array gibi,Psot metodunda bir boolean değer gibi, vs....)
Örnek:
Kod: Tümünü seç
procedure TWebModule1.WebModule1CariAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
Son olarak:
--Bu iki model birbirinin alternatifidir anladığım kadarıyla.Bu doğrumudur?
--Geliştireceğim Rest Api ye her dilde ve her platformda yazılacak her hangi bir uygulamadan erişilebilmesini isterim.
Bu durumda benim 2.Modeli kullanmam gerekir gibi geliyor bana.
Sizce bu doğrumudur?
Yada yukarıda yazdıklaım da mantık hatası veya yanlış değerlendirilen hususlar varmıdır?
Biraz uzun oldu kusura bakmayın ama, daha önceki projelerimde yaşadığım refactoring zorluklarını en aza indirebilmek için belki biraz fazla kastırmış olabilirim.
Bu konuda bilgi ve tecrübesi olan arkadaşlar bu husularda bilgilendirebilirler ise çok sevinirim.
Herkese iyi çalışmalar dilerim.