Merhaba Arkadaşlar,
Aslında bu yazımda XMLHttp nesnesi ile POST işlemlerini ele almayı düşünüyordum. Ancak konu ile ilgili ilk yazımdan sonra sizlerden bir çok fikir ve istek geldi. Yazının sizler tarafından okunması ve ilgi görmesi beni çok mutlu etti. Bu yüzden şu anda okuyacağınız makaleyi bu istekler ve öneriler doğrultusunda şekillendirdim, daha doğrusu siz şekillendirdiniz. Paylaşım noktam HTTP POST'un dışında olacak ancak yakın zamanda bundan da bahsedeceğim. Sizlerden gelen geri dönüşler de jQuery ile ajax işlemlerinin özdeşleştirildiğini gördüm. Bunun çok doğru bir yaklaşım olduğunu düşünmüyorum. jQuery'ye aynı garbage collector'e yapılan muamele yapılıyor gibi geldi bana. jQuery'ye bağlı kalmadan kendi ajax kütüphanenizi oluşturabilir ve kullanabilirsiniz. jQuey ajax işlemleri için bir sarmalayıcıdır ayrıca ajax işlemlerini ele alabileceğiniz popüler bir çok kütüphane mevcuttur. Bu kütüphaneleri kullanarak farklı browser sorunlarından korunmuş olursunuz. Uğur UMUTLUOĞLU'nun JQuery ile Ajax işlemlerini anlatan güzel bir yazısı mevcut. Bu yazıyı okumanızı ve orada verilen linkleri incelemenizi şiddetle tavsiye ederim. Şunu ısrarla tekrar dile getirmek istiyorum, XMLHttp'yi ve kullanımını anlamamız (frame, iframe, image, cookie gibi ajax tekniklerini de öğrenmenizi tavsiye ederim), ajax ile ilgili hazır kütüphaneleri daha iyi anlamamızı ve onlara daha bilinçli gözlerle yaklaşmamızı sağlayacaktır. Bir başka deyişle; kendimizi "direksiyon şoförü" deyimini ötesine taşımış oluruz, az buçuk motordan da anlıyor oluruz.
Sizlerle bu makalemde az önce de dediğim gibi POST işlemlerinden bahsetmek istiyordum ancak ilk makalemde anlattığım GET taleplerine bir U döünüşü yapacağım. Bir farkla, JSON farkı ile. Yani GET taleplerini JSON ile nasıl ele alabileceğimizden bahsedeceğim. Aynı zamanda konu ile ilgili arada kalan kırıntıları da topluyor olacağız. XMLHttp nesnesi yerine XMLHttpRequest nesnesini kullanacak ve taleplerimizin cache'lenmelerinin nasıl önüne geçebileceğimizden bahsediyor olacağız. Aynı zamanda ajax taleplerinin kuyruklanması gerekliliğinden de bahsedeceğiz.
İlk önce JSON'dan biraz bahsetmekte fayda var. Detaylı bilgiyi json'in kendi internet sitesinden edinebilirsiniz ancak basitçe üzerinden geçmek gerekirse "JSON (JavaScript Object Notation) hafif bir veri değişim formatıdır - Kaynak: json.org". Aslına bakarsanız bir XML alternatifidir. XML'den daha az yer kapladığı ve javascript içerisinde her hangi özel bir parse işlemine tabi tutulmadan direk kullanılabildiği için son derece geçerli sebeplerden dolayı popüler olmuştur. JSON notasyonu "key:value" şeklindedir. Bir örmek üzerinde inceleyecek olursak;
{
"kitaplar":
{
"kitap":
{
"kitap_adi" : "Düşlerin Efendisi",
"yazar_adi" : "Jules Verne",
"sayfa_sayisi" : "1128"
}
"kitap":
{
"kitap_adi" : HOBBIT",
"yazar_adi" : "J. R. R. Tolkien",
"sayfa_sayisi" : "426"
}
}
}
Yukarı da ki JSON notasyonunun XML karşılığı şudur;
<kitaplar>
<kitap>
<kitap_adi>Düşlerin Efendisi</kitap_adi>
<yazar_adi>Jules Verne</yazar_adi>
<sayfa_sayisi>1128</sayfa_sayisi>
</kitap>
<kitap>
<kitap_adi>HOBBIT</kitap_adi>
<yazar_adi>J. R. R. Tolkien</yazar_adi>
<sayfa_sayisi>426</sayfa_sayisi>
</kitap>
</kitaplar>
Gördüğünüz gibi XML'i çıplak gözle takip etmek daha kolaydır. Ancak JSON'ı da javascript ile birlikte kullanmak daha kolaydır ve ağırlıkça daha hafiftir. Bu sayede de network trafiğinize faydalıdır. JSON'ı nasıl kullancağımızdan bahsetmeden önce XMLHttpRequest nesnesinden de bahsetmek istiyorum. Yazının ilerleyen kısımlarına geçmeden önce konu ile ilgili bir önce ki makalemi okumanızı tavsiye ederim. XMLHttpRequest nesnesinin çıkış noktası şudur; XMLHttp nesnesinin büyük kullanım kolaylıklarına sahip olduğunu gören diğer browser yapıcıları bu nesneyi XMLHttpRequest adı ile kendi bünyelerine aldılar (XMLRequest nesnesi ile aynı davranışa sahip kendi native nesnelerini yarattılar). Microsoft'ta genele uymak adına IE7 sürümünden itibaren kendi native XMLHttpRequest nesnesini oluşturdu. Bir önceki makalemde "createXHR()" isimli bir fonksiyon yazarak XMLHttp nesnesini oluşturmuş ve kullanmıştık. Şimdi bu fonksiyonu XMLHttpRequest nesnesini de kapsayacak şekilde yeniden ele alalım.
function createXHR()
{
// Tarayıcımızın ActiveXObject desteği olup olmadığını kontrol ediyoruz.
if(typeof ActiveXObject != "undefined")
// Nesne versiyonları bir dizi içersine alınıyor.
var versiyonlar = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0"];
var XHR = null;
try
{
XHR = new XMLHttpRequest();
}
catch (err1)
{
try {
// Döngü ile her versiyon en son sürümden başlamak
// üzere oluşturulmaya çalışılıyor.
for (var i = 0; i < versiyonlar.length; i++)
{
var XHR = new ActiveXObject(versiyonlar[i]);
}
}
catch (err2)
{
// İhtiyaçlarınıza uygun şekilde istisnaları yönetebilirsiniz.
}
}
return XHR;
}
Kod bloğuna hızlıca göz atılacak olursa ilk önce XMLHttpRequest nesnesinin oluşturulmaya çalışıldığı, başarısız olunması durumunda XMLHttp ActiveX objesine dönüş yapıldığı görülür. Bu şekilde bir çok browser'ın desteği sağlanmış olur. Konuyu buraya kadar getirdikten sonra bir HTTP 1.1 kısıtlamasından söz etmek istiyorum. Bu kısıtlama bir etki alanında aynı anda yalnızca 2 talebin ele alınabileceği kısıtıdır (aslında subdomain'ler üzerinden talepleri yönlendirerek bu durumun kısmen önüne geçebilirsiniz). Bu kısıtın amacı server'larda oluşabilecek aşırı talep yükünün önüne geçmektir. Bu kısıt doğal olarak XHR (XMLHttpRequest nesnesi bundan sonra bu şekilde anılacak) taleplerimizi ele alma yöntemlerimizi de etkileyecektir. Bunun açık anlamı şudur; Aynı anda sadece 2 adet Http talebini ele almalısınız, kesinlikle daha fazla olmamalı. Bu durumu yönetebileceğiniz çeşitli kütüphaneler mevcut. İnternette "ajax queue" olarak yapacağınız bir arama ile bir çok sonuca ulaşabilirsiniz. Eğer isterseniz kendi kuyruk sisteminizi kendiniz de kodlayabilirsiniz. Ne yapmanız gerektiğinizi siz tercih etmelisiniz.
Ajax işlemleriniz de cache ile de mücadele etmemiz gerekir. Lakin aynı url'e aynı parametreler ile yapılacak talepler istemci tarafında cache'lenecek ve talep sonucu değişse bile veriler istemcinin cache'inde alınacağı için kullanıcı her talep sonucu aynı değerleri görme riski ile karşı karşıya kalacaktır. Bu durumun önüne geçmenin bir yolu vardır, header'ları kullanmak. Eğer talebimizin "Cache-Control" header değerine "no-cache, no-store, must-revalidate", "Pragma" header'ına "no-cache" ve son olarak "Expires" header'ına geçmiş bir tarih ataması yapabilirsek bu sorunu bir çok browser için çözmüş oluruz. Ancak ASP.NET için bir ayrıntı söz konusu. Talebin karşılandığı (ele alındığı) Page'in "PreInit" event'in de sayfanın cache'lenmemesi gerektiği söylenmeli. Peki bu nasıl yapılabilir? Cevap "Response. CacheControl" property'sin de yatıyor. Bunun değerini "no-cache" verirsek sanırım artık konu ile ilgili sorun kalmamış olur.
XHR'nin bir kusuruna da değinmedeb geçmemek lazım. Farklı etki alanlarına talepte bulunamayacağınız gerçeği ile yüz yüzesiniz. Konu ile ilgili "The Complete Reference AJAX - Thomas A. Powell" kitabından bir alıntı yapacak olursak, "URL talepleri sunulan sayfa ile (talep yapılan) aynı port numarası ve aynı protokol ile aynı domain içerisinde olmaladır". Yani XHR taleplerini kullanarak www.request.com sayfasından www.response.com sayfasına talepte bulunamazsınız. Hatta www2.request.com subdomain'ine de talepte bulunamazsınız hatta ve hatta www.request.com adresinden www.request.com:80 adresine de talepte bulunamazsınız. Bu "Same-Origin Security" nin bir sonucudur. Faklı domain alanlarına başvuruda bulunmak için frame kullanabilir yada başka ajax kütüphanelerinden yardım alabilirsiniz.
O halde hemen bir örnek üzerinde anlatılanları ele alalım. Konu ile ilgili ilk makalem de kullanmış olduğum örneği burada da kullanacağım. Ancak bu sefer personelin yalnızca mail adresini değil telefon numarasını da alacağız. Bunu yapmak için ise JSON desteğinden faydalanacağız. Ancak server'da json notasyonuna uygun hale getirilmiş string değerimizi client tarafında javascript içerisinde json nesnesi olarak ele alabilmemiz için json_parse.js dosyasını projemize eklemeliyiz. Bu dosya da ki "json_parse" isimli metod sayesinde json notasyonuna uygun olarak oluşturulmuş string değerleri json nesnesi olarak ele alabileceğiz. Server tarafında client'a göndereceğiniz değerleri json formatına sokabilmenizi sağlayan class seviyesinde kütüphaneler de mevcut, isterseniz server taraflı kodlarınız da bunları da kullanabilirsiniz ancak ben bu işi hard-code kendim yapacağım.
İlk önce client tarafında talebimizi nasıl yaratacağımıza bakalım;
function StartRequest() {
var XHR = new createXHR();
var divSonuc = document.getElementById("ad") + "=" + encodeURIComponent(document.getElementById("txtAdi").value) +"soyad") + "=" + encodeURIComponent(document.getElementById("txtSoyadi").value);
XHR.open("DataPage.aspx?" + parametreler, true);
XHR.setRequestHeader("Cache-Control", "no-store, no-cache, must-revalidate");
XHR.setRequestHeader("Pragma", "no-cache");
XHR.setRequestHeader("Expires", "Sat, 1 Jan 2000 00:00:00 GMT");
XHR.onreadystatechange = function () {
if (XHR.readyState == 4 && (XHR.status == 200 || XHR.status == 304)) {
try {
var jsonObject = json_parse(XHR.responseText);
if (jsonObject.durum == "1") {
divSonuc.innerHTML = "Personele ait kayıtlı bilgi yok.";
}
else {
divSonuc.innerHTML = "<b>E-Posta Adresi:</b> " + jsonObject.email + "<br/><b>Telefon:</b> " + jsonObject.telefon;
}
}
catch (err) {
divSonuc.innerHTML = "Sonuç JSON formatında değil";
}
}
}
XHR.send(null);
}
Burada yapılanı şu şekilde özetleyebiliriz; İlk önce bir XHR nesnesi oluşturduk ve arkasından nesnemiz ile "DataPage.aspx" isimli url'e asenkron bir "get" talebi başlatacağımızı söyledik. Hemen arkasından taleplerimizin cache'lenmesini engellemek için çeşitli header'lara değerler atadık. Daha sonra server'dan gelecek olan json formatlı değeri takip edebilmek için onreadystatechange eventimiz içerisinde readyState ve status property'lerini kontrol ederek gerekli değerlerin sağlanması durumunda dönen sonucu sayfamıza aldık. Sanırım dikkatinizi çekmiştir; json nesnemizin "durum" isimli değerinin "1" olmaması şartını arıyoruz. Server tarafında "durum" değerlerini ayarlarken "1" in veritabanın da tpersonele ait her hangi bir bilgi bulunamaması durumunu temsil etmesini sağladık. Bu şekilde client tarafında server'dan dönen değerleri daha sağlıklı bir şekilde ele alabildik. Tabii bu olayları tetikleyebilmek için "StartRequest()" metodunu sayfamızda ki bir butonun click event'ine bağlamalıyız.
Server tarafına geçecek olursak ilk makale de ki örnekte yapmış olduklarımızdan çok ta farklı şeyler yapmadığımızı göreceğiz. İstemciden gelen QueryString değerlerimize göre AdventureWorks isimli veritabanımızın Person.Contact isimli tablosunda arama yaptık ve ilk örneğimizden farklı olarak dönen değerleri json fotmatında istemciye gönderdik ayrıca sayfanın cache'lenmesini engellemek için PreInit eventin de CacheControl property'sine "no-cache" değerini atadık. İsterseniz gelin şimdi server tarafında ki kodlarımıza bir göz atalım.
protected void Page_Load(object sender, EventArgs e)
{
Page.Response.ContentType = "text/Plain";
SqlConnection connection = null;
SqlCommand command = null;
SqlDataReader reader = null;
try
{
string ad = Page.Request.QueryString["ad"].ToString();
string soyad = Page.Request.QueryString["soyad"].ToString();
connection = new SqlConnection("server=TOLGAAYKURT;database=AdventureWorks;integrated security=SSPI");
command = new SqlCommand("SELECT EmailAddress, Phone FROM Person.Contact WHERE FirstName=@ad AND LastName=@soyad", connection);
command.Parameters.AddWithValue("@ad", ad);
command.Parameters.AddWithValue("@soyad", soyad);
connection.Open();
reader = command.ExecuteReader();
String email = String.Empty;
String telefon = String.Empty;
while (reader.Read())
{
email = reader["EmailAddress"].ToString();
telefon = reader["Phone"].ToString();
}
reader.Close();
connection.Close();
// JSON formatına uygun notasyon ile verilerimizi gönderiyoruz. Verilerinizi JSON formatına
// çeviren hazır kütüphaneler de kullanabilirsiniz.
string durum = "0";
if (email == String.Empty && telefon == String.Empty) durum = "1";
Page.Response.Write("{\"email\":\"" + email + "\",\"telefon\":\"" + telefon + "\",\"durum\":\"" + durum + "\"}");
}
catch(Exception ex)
{
// Oluşabilecek hataları yönetebilirsiniz.
}
finally
{
if (connection != null && connection.State != System.Data.ConnectionState.Closed) connection.Close();
if (reader != null && reader.IsClosed == false) reader.Close();
connection.Dispose();
command.Dispose();
reader.Dispose();
connection = null;
command = null;
reader = null;
}
}
void Page_PreInit(object sender, EventArgs e)
{
// Sayfanın cache'lenmesi engellendi.
Page.Response.CacheControl = "no-cache";
}
İstemciden gelen değerlere göre database sorgumuzu yaptıktan sonra sorgu sonucuna uygun bir "durum" değişkeni tanımlıyor ve database'den dönen verilere uygun değerini veriyoruz. Arkasından json formatına uygun string'imizi oluştuyor ve istemciye gönderiyoruz. Tabii "Page_PreInit" te yaptığımız "CacheControl" değer atamasını da es geçememek lazım.
Görüldüğü gibi json yardımı ile taleplerimize yanıt vermek hiç te zor değil. Hatta post taleplerini gönderirken send metoduna parametre olarak geçeceğimiz form verilerini de json formatında sunucuya gönderebiliriz.
Bu makale de amacım ilk makalede eksik kalmış olan yerleri doldurmaktı. Aslında bunu post işlemlerini anlatırken yapmayı planlamıştım fakat çok uzun ve dikkat dağıtıcı bir yazı olacağını düşündüm ve ayrı olarak ele almayı doğru buldum. Kaynak kodları bilgisayarınıza indirerek incelemenizi tavsiye ederim.
Paylaşımlarımı yakın zaman da www.tolga-aykurt.com blog adresimden de takip edebilirsiniz.
Selamlar.
Örnek uygulamanin kodlarini indirmek için tiklayiniz...