16 Şubat 2009 Pazartesi

State Management - 2

Merhaba bir kez daha herkese. State Management konulu makalemizin ikinci bölümü ile kaldığımız yerden devam edeceğiz.

İlk bölümde State Management'ın ne demek olduğunu, neden ihtiyaç duyulduğunu incelemiş, ViewState konusunu ele almıştık. Şimdi ise diğer nesnelerimize değineceğiz. Cookie ile başlayalım isterseniz.

Cookies: Cookie, kelime anlamı olarak çerez. :) Nedir bu cookieler? Cookieler, kullanıcılar hakkında bilgileri saklayan zararsız dosyalardır. Server tarafından kullanıcının bilgisayarına gönderilir. Aslında bu en basit haliyle böyle. Birazdan tüm detaylarına gireceğiz. Ancak bu kavramı anlamanız için somut bir kaç örnek vermek istiyorum.

Örneğin bir bir foruma üyesiniz. Foruma en son ne zaman geldiğiniz, siz yokken hangi mesajlar atılmış cookie ile görülebilir. Cookie'den geliş tarihiniz bulunur ve uygun işlem yapılır. Başka bir örnek verelim. E-Posta hesaplarında "Beni Hatırla" seçeneği var. Beni Hatırla seçeneğini aktifleştirirseniz, o da örneğin cookie yardımıyla sizi tanır. Arkadaşlar dikkatinizi çekerim, bu verdiğim örneklerde cookie kullanımı çok yaygın demiyorum. Sadece cookie ile de olur diyorum. Mesela online alışverişteki alışveriş sepeti uygulaması. Siz siteden çıksanız da sepetiniz bir sonraki açılışta hala orada durur. Cookie ile bunu yapmak pek akıllıca değil(sebebini makaleyi okudukça anlayacaksınız), genelde de yapılmaz ama mantığını anlamanız açısından size fikir verecektir.

Cookie hakkında kafanızda bir şeyler canlandıysa şayet şimdi bir adım öteye gidip, genel yapısını inceleyelim. Kullanıcı herhangi bir siteye girdi diyelim. Sizin sitenize girmiş olsun. Siz, sitenizden ona cookie'yi atarsınız(Response ile atabilirsiniz, çünkü hatırlarsanız ASP.Net'te server'dan dönen cevap response idi, bu yüzden cookie'yi yazarken response nesnesinden yardım alabiliriz.). Farzı misal kullanıcı sitenize tekrar girmeye karar verdi.URL'yi girdiği zaman, kullanıcının tarayıcısı(IE, Mozilla, Opera, Chrome vs.) da sitenize cookie'yi atar. Yani sizin kullanıcının bilgisayarına attığınız cookieyi, kullanıcının tarayıcısı yardımıyla geri alırsınız(Request ile alabilirsiniz, yine hatırlarsanız ASP.Net't client'tan giden istek Request idi, bu yüzden cookie'yi aldığımızda request nesnesinden yardım alabiliriz.). Yalnız tarayıcı URL ile ilgili cookie varsa, bulursa atar. Yoksa, atamaz. Böylelikle siz, sitenize gelen kullanıcı ile ilgili bazı bilgileri edinmiş olursunuz.
Kullanıcının siteden hangi sayfayı istediği normalde önemli değildir. Aslında önemli olduğu durumlar var ancak buna da makalemizin ilerleyen satırlarında değineceğiz. Standart olarak, cookie siteniz ile ilşkilidir, sitenizdeki bir sayfa ile değil(İsterseniz farklı ilişkiler de yaratabilirsiniz. Birazdan birlikte yaratacağız zaten.).

Çoğu tarayıcılar 4096 byte'a kadar cookie destekler. Bu küçük limit yüzünden, cookielerde küçük veriler saklamak uygundur. Tarayıcılar, bir siteden kullanıcının ne kadar cookie alacağını da belirlerler. Bir çok tarayıcı site başına 20 cookie'ye izin veriyor. Eğer siteniz daha fazlasını gönderirse eski cookieler sırasıyla silinmiş olur. Ancak bir kısım tarayıcılar da genel bir limit belirliyor, ki genelde 300 cookie oluyor bu limit, site bazında değil de tüm sitelerden gelen cookie sayısına göre saklıyorlar.

Cookie sınırlandırmalarında en büyük sorun ise kullanıcının tarayıcı ayarlarından cookie alımını engellemesidir. Gerçi bazı yazılımcılar, cookie'yi reddediyorsa siteme girmesin, de diyebilmekte. ? Dolayısıyla yararlı da olsa uygulamalarınız cookielere bağımlı olmamalıdır. Alternatif yollarınız mutlaka olmalıdır. Veyahut diğer yöntemlerle verilerini taşıyıp, saklarsınız. Kritik verilerde cookie kullanmamanız gerektiğini de herhalde fark etmişsinizdir. Bu konuya birazdan tekrar gireceğim. Tarayıcı ayarlarında yapılacak değişiklikler her zaman güçlük yaratır dedik. Bu sorunu aşabilmenin bir yolu da sitenizde P3P(Platform for Privacy Preferences Project) tanımlamaktır. Bu sayede, tarayıcılarında belli policy ayarları yapmış kullanıcılar, policy ihlallerinde derhal uyarılır ve siteye girmeyebilirler. Bu durumu sağladığınızda tarayıcılar sitenizden cookie alımı yapabilirler.

Cookie Yazma
İsterseniz şimdi cookie yazma olayına geçelim. Cookieler, kullanıcıların tarayıcılarına (yukarıda bahsettiğim sebeplerden dolayı) HttpResponse nesnesi ile yollanırlar, ki bu nesnenin de Cookies adında bir koleksiyonu vardır. HttpResponse nesnesine Page sınıfımızın(yani sayfamızın), Response property'si ile ulaşabiliriz. Sitenizden, kullanıcının harddiskinde depolanmak üzere, tarayıcıya gönderilen her cookie'yi Cookies koleksiyonuna eklemelisiniz.

Cookie yaratınca bir Name ve Value vermelisiniz. Mantıksal olarak anlamanız için ViewState'te bahsettiğim(State Management-1 Makalesi) HashTable yapısı gibi düşüneblirsiniz. Cookie'nin de "Name"i unique(eşsiz,tek) olmalı. Bu sayede direkt o isme tekabül eden değeri de bulabiliriz. Siz kazara aynı isimde 2 cookie yaratırsanız, birini diğerinin üstüne yazar. Bunlardan başka Cookie'nin yaşam süresini de belirleyebilirsiniz(expiration date and time). Mesela bir cookie'nin expiration date'ini 10 gün olarak ayarlarsanız, kullanıcı 10 gün boyunca o siteye girmezse cookie silinir. Kullanıcının siteye son girişinden itibaren 10 gün dikkate alınır çünkü. Şimdi diyebilirsiniz cookie'nin ömrünü uzun tutarım diye ama bunun da %100 garantisi yok çünkü kullanıcının canı sıkılır ve gider tüm cookieleri siler. :) Bu yüzden cookielerde öenmli şeyler taşınmamalı demiştik yukarıda.

Eğer cookie'nin expiration time'ını ayarlamazsanız, cookie yaratılır fakat kullanıcının bilgisayarında saklanmaz. Daha sonra göreceğimiz Session gibi varlığını sürdürür. Kullanıcı tarayıcısını kapatırsa cookie de gider. Bu yüzden expiration time'ını ayarlamayı unutmamalısınız. Ancak bu şekilde kullanılabilecek cookieler de işe yarayabilir; ya da kullanıcının bilgisyaraına cookie yazmayı güvenli bulmayabilirsiniz. Bu durumlarda Expiration Time değerine girmez ve kalıcı olmayan cookieler yaratırsınız. Örneğin ortak kullanılan bir bilgisayarınız vardır, girdiğiniz sitelerin cookiesini istemeyebilirsiniz.

Cookieleri eklemenin yollarından iki tanesine bakalım şimdi:
Response.Cookies["userName"].Value = "gurkan";
Response.Cookies["userName"].Expires = DateTime.Now.AddDays(1);

Yukarıdaki yol ile Cookilere direkt değer verebilirsiniz. Cookieler, NameObjectCollectionBase'den türedikleri için bu şekilde değer verilebilirler. UserName adında bir cookie yaratılıyor ve ona değer olarak gurkan veriliyor. Eğer o siteye 1 gün içinde tekrar girmezseniz cookie siliniyor.

HttpCookie cerez = new HttpCookie("lastVisit");
cerez.Value = DateTime.Now.ToString();
cerez.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(cerez);

Yukarıdaki yol ile de cookie'yi yaratmak için HttpCookie sınıfını kullandık. Bu şekilde kullanmak için constructor içinde cookie adını belirtmek zorundayız. Dikkat edilmesi gereken nokta: tüm cookie değerleri string olmak zorundadır. Expiration ise DateTime türünde olmalıdır.

Birden Fazla Değeri Olan Cookie Olur mu?

Yukarıdaki örneklerde de gördüğünüz üzere her cookie'de bir değer sakladık. Ancak bir cookie'de birden fazla name-value çifti saklayabilirsiniz. Name-Value çiftleri subkeys olarak adlandırılırlar. Örneğin; "username" ve"lastvisit" adında 2 farklı cookie saklamak yerine, "userInfo" adında tek bir cookie yaratabiliriz. "username" ve"lastvisit" adında da subkey'leri olur.
Subkey kullanmanın birçok sebebi olabilir. Öncelikle birbiriyle ilişkili bilgileri tek bir cookie'ye koymak gayet güzel bir düşüncedir. Bunun dışında mesela hepsine birden expiration süresini atayabilirsiniz (Tersten düşünürsek, eğer hepsine farklı farklı süreler atayacaksak, ayrı ayrı cookieler kullanmak daha mantıklıdır). Ayrıca tarayıcıların limitlendirmelerini de bir nebze olsun aşabiliriz bu yolla.

Subkeys ile cookie yaratmak için yine 2 yolu da görelim:
Response.Cookies["userInfo"]["userName"] = "gurkan";
Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);

Yukarıdaki örnekte önceki örnektekilerin aynısı yapılıyor ama gördüğünüz gibi userinfo'nun alt anahtarları olarak kullandık username ve lastvisit'i.

HttpCookie cerez = new HttpCookie("userInfo");
cerez.Values["userName"] = "gurkan";
cerez.Values["lastVisit"] = DateTime.Now.ToString();
cerez.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(cerez);

Yukarıdaki örnekte bir önceki örnekle aynı işi HttpCookie nesnesi yardımıyla yapmakta.

Cookie Faaliyet Alanları
Daha önce bahsettiğimiz bir konuya geri dönelim. Normalde tüm siteler kullanıcılarına cookie'yi atarlar. Kullanıcı da bu siteye request gönderirse, bu cookiler server'a gider. Siteye geri dönmüş olur. Yani başka bir deyişle: o sitedeki tüm sayfalar cookie'leri alır. Cookielerin faaliyet alanlarını 2 yolla ayarlayabilirsiniz.
1) Server'da bir klasöre erişim hakkı verirsiniz.
2) Domainlerin faaliyetlerini belirlersiniz.
Şimdi bu iki yaklaşımı da inceleyelim.

Server'da bir klasöre erişim hakkı vermek: Server'da bir klasör açarsınız ve cookie'leri alabilecek sayfaları o klasör altında tanımlarsınız. Bunu da Cookie'nin Path property'si ile ayarlarsınız.
HttpCookie cerez = new HttpCookie("AppCookie");
cerez.Value = "written " + DateTime.Now.ToString();
cerez.Expires = DateTime.Now.AddDays(1);
cerez.Path = "/Application1";
Response.Cookies.Add(cerez);
Buradaki Path, root'un altında fiziksel bir yol olabileceği gibi virtual path de olabilir. Yukarıdaki örnekte, Application1 klasörü içindeki tüm sayfalar cookielere erişebilmekte. Örneğin: www.gurkanalkan.com/Application1/... içindekiler cookileri alır. Ancak: www.gurkanalkan.com ya da www.gurkanalkan.com/Application2/... içindekiler cookilere erişemez.
(Yukarıdaki örneği diğer yol ile de yapabilirdik.)
Bazı tarayıcılarda path, büyük-küçük harfe karşı duyarlıdır. Buna dikkat edilmesi gerekir.

Domaine Scope Vermek: Bunda da domaininize göre erişim hakkı verirsiniz. Cookie'nin Domain property'si ile ayarlanır.

Response.Cookies["domain"].Value = DateTime.Now.ToString();
Response.Cookies["domain"].Expires = DateTime.Now.AddDays(1);
Response.Cookies["domain"].Domain = "support.gurkanalkan.com";

Yukarıdaki örnekte, cookielere sadece support.gurkanalkan.com subdomaini erişebilir. Diğer domainlerden erişilebilemez.
Son satırı:
Response.Cookies["domain"].Domain = "gurkanalkan.com";
şeklinde değiştirirseniz, tüm subdomainlerden erişilebilir bir hale gelmiş olur. Örneğin; "support.gurkanalkan.com", " forum.gurkanalkan.com", "thisishalloween.gurkanalkan.com" gibi tüm alt domainler erişir.


Cookie Okuma

Cookieleri HttpRequest nesnesi ile okuruz. Page sınıfımızın(sayfamız tabi ki) Request nesnesi ile de ulaşabiliriz o nesneye. Daha önce bu işlemin nasıl olduğuna değindiğim için direkt olarak kod üzerinde göstereceğim.

if(Request.Cookies["userName"] != null)
Label1.Text = Server.HtmlEncode(Request.Cookies["userName"].Value);

Yukarıdaki örnekte yine direkt işlem yapılmış. Eğer username isminde bir cookie varsa, bunun değerini label1'e yazdırıyor. HtmlEncode metodu da parametresindeki metni eğer html kodları varsa, değiştirerek komut olarak görünmemesini sağlar.

if(Request.Cookies["userName"] != null)
{
HttpCookie cerez= Request.Cookies["userName"];
Label1.Text = Server.HtmlEncode(cerez.Value);
}

Yukarıda da aynı işlemi diğer yolla yaptık.

Burada dikkat edilmesi gekeren, cookieyi okumadan önce, o cookienin var olduğundan emin olmaktır. Eğer bunu kontrol etmezsek ve okumaya çalıştığımız cookie yoksa(mesela anahtarını[ismini] yanlış yazmışızdır.) NullReferenceException alırız.
HtmlEncode ile de encode yaparak, kötü niyetli kullanıcıların cookie üzerinde oynama yapmamalarını sağlıyoruz.
Farklı tarayıcılar cookieleri farklı şekillerde saklarlar. Aynı makinadaki farklı tarayıcılar bile birbirlerinin cookielerini okuyamazlar. Örneğin IE'nin sakladığı cookielere Mozilla'dan erişemezsin.

Subkey'i olan bir cookiden okuma yapalım.

if(Request.Cookies["userInfo"] != null)
{
Label1.Text =
Server.HtmlEncode(Request.Cookies["userInfo"]["userName"]);

Label2.Text =
Server.HtmlEncode(Request.Cookies["userInfo"]["lastVisit"]);
}

Gördüğünüz gibi yine önce böyle bir cookienin var olup olmadığına bakıyoruz. Daha sonra da cookie yazma konusunda da gördüğümüz yöntemi kullanarak bu sefer okuma yapıyoruz.
"lastvisit"i daha önce DateTime türünde kullanmıştık. Ancak cookieler string değer tuttuklarından yukarıdaki gibi kullanılmış.

DateTime dt;
dt = DateTime.Parse(Request.Cookies["userInfo"]["lastVisit"]);
şeklinde de değiştirebiliriz.

Daha önce de bahsettiğim gibi subkey'ler NameValueCollection idi. Yani bir koleksiyon. Dolayısıyla bu koleksiyondan subkeyi farklı bir şekilde de alabiliriz. (Name'inden value'sini ayırırız)

if(Request.Cookies["userInfo"] != null)
{
System.Collections.Specialized.NameValueCollection
UserInfoCookieCollection;

UserInfoCookieCollection = Request.Cookies["userInfo"].Values;
Label1.Text =
Server.HtmlEncode(UserInfoCookieCollection["userName"]);
Label2.Text =
Server.HtmlEncode(UserInfoCookieCollection["lastVisit"]);
}


"Expiration Date"i Değiştirmek

Tarayıcıların cookieleri yönetmekten sorumlu olduğunu söylemiştik(Server ile cookile alışverişi, bilgisayara yerleştirmesine izin vermes, vermemesi, limitlendirmeleri). Cookienin expiration time ve date'i de tarayıcya bu konularda yardım eder(saklanmaları, silinmeleri vs.). Dolayısıyla siz cookienin Name ve Value'suna erişip okuyabilirken, expiration Time ve Date'ini okuyamazsınız.
Tarayıcı, server'a cookie'yi yollarken, expiration bilgisini göndermez(sıfır değeri döndürür). Eğer cookienin expiration date'i ile ilgili şüpheleriz varsa cookie'yi değiştirebilirsiniz. (Birazdan cookie değiştirme konusuna da değineceğiz.)


Cookie Koleksiyonunu Okumak

Bazen sayfanızda geçerli olan tüm cookieleri okumak durumunda kalabilirsiniz. Aşağıdaki kod örneği de bu durumlar içindir.

System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie cerez;
for(int i=0; i
{
cerez = Request.Cookies[i];
output.Append("Cookie name = " + Server.HtmlEncode(cerez.Name)
+ "
");

output.Append("Cookie value = " + Server.HtmlEncode(cerez.Value)
+ "

");

}
Label1.Text = output.ToString();


Yukarıdaki örneğin bir problemi var. Eğer cookielerin subkeyleri varsa, subkeyler tek bir name/value stringi şeklinde görünecektir.

Subkey'in olup olmadığını anlamak için HasKeys property'si kullanılır. Eğer varsa; subkey koleksiyonunu, ayrı ayrı subkey name ve value'ları şeklinde okuyabilirsiniz. Subkey value'larını Values koleksiyonundan indeks ile direkt okuyabilirsiniz.Value'ların isimlerine de Values koleksiyonunun AllKeys property'si ile okuyabilirsiniz. Bu size bir string dizisi dönecektir. Aynı şekilde Name'leri Keys property'si ile de okuyabilirsiniz. Bu da size bir string dizisi döner. Ancak aralarında bir fark var. AllKeys ile isimler siteye ilk erişimden sonra cachelenir(önbellekleme) . Keys ile isimler siteye her erişimde tekrar tekrar bulunur. Bu yüzden şartlara göre her ikisini de kullanmak avantajlı olabilir.

Şimdi bir önceki örneği subkey durumunu kontrol edecek şekilde değiştirelim.

System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie cerez;
string subkeyName,subkeyValue;
for(int i=0; i
{
cerez = Request.Cookies[i];
output.Append("Name = " + cerez.Name + "
");

if(cerez.HasKeys)
{
for(int j=0; j
{
subkeyName = Server.HtmlEncode(cerez.Values.AllKeys[j]);
subkeyValue = Server.HtmlEncode(cerez.Values[j]);
output.Append("Subkey name = " + subkeyName + "
");

output.Append("Subkey value = " + subkeyValue +
"

");

}
}
else
{
output.Append("Value = " + Server.HtmlEncode(cerez.Value) +
"

");

}
}
Label1.Text = output.ToString();


Yukarıdaki kodu NameValueCollection nesnesi yardımıyla da yapabilirdik.

System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie cerez;
string subkeyName,subkeyValue;
for(int i=0; i
{
cerez = Request.Cookies[i];
output.Append("Name = " + cerez.Name + "
");

if (cerez.HasKeys)
{
System.Collections.Specialized.NameValueCollection CookieValues =
cerez.Values;
string[] CookieValueNames = CookieValues.AllKeys;
for (int j = 0; j <>
{
subkeyName = Server.HtmlEncode(CookieValueNames[j]);
subkeyValue = Server.HtmlEncode(CookieValues[j]);
output.Append("Subkey name = " + subkeyName + "
");

output.Append("Subkey value = " + subkeyValue +
"

");

}
}
else
{
output.Append("Value = " + Server.HtmlEncode(cerez.Value) +
"

");

}
}
Label1.Text = output.ToString();


Cookieleri Değiştirme ve Silme

Bir cookieyi direkt değiştiremezsiniz. Bunun yerine, yeni bir değer verip tekrar cookieyi yaratırsınız ve onu tarayıcıya gönderirsiniz. Eskisinin üzerine yazdırmış olursunuz.
Aşağıda da örnek olarak ziyaretçi sayısını tutan bir kod mevcuttur.
int sayac;
if (Request.Cookies["counter"] == null)
sayac = 0;
else
{
sayac = int.Parse(Request.Cookies["counter"].Value);
}
sayac++;

Response.Cookies["counter"].Value = sayac.ToString();
Response.Cookies["counter"].Expires = DateTime.Now.AddDays(1);

Silme işlemine geçelim isterseniz. Silme de bir nevi değiştirme işlemidir. Cookieyi sizin direkt silmeniz mümkün değil çünkü cookie kullanıcının bilgisayarında saklı. Cookieyi siz değil, kullanıcının tarayıcısı silecek. Ama bunu siz sağlayacaksınız. Silmek istediğiniz cookie ile aynı isimde bir cookie oluşturun; ama expiration time'ını geçmiş bir tarih olarak ayarlayın. Bunu gönderin. Tarayıcı da bunu diğerinin üstüne yazacak haliyle aynı isimde olduğundan. Daha sonra da expiration time'ı dolduğu için de silecek. :)
Aşağıda örnek bir kod yer almaktadır.

HttpCookie cerez;
string cookieName;
int limit = Request.Cookies.Count;
for (int i=0; i
{
cookieName = Request.Cookies[i].Name;
cerez = new HttpCookie(cookieName);
cerez.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(cerez);
}

Şimdi de değiştirme ve silme işlemlerini subkeyi olan cookieler için yapalım.
Herhalde genel mantığı anlamışsınızdır. Bu yüzden değiştirme işlemini direkt kod üzerinden gösereceğim.
Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);


Subkeyleri silmek içinse,subkeyleri tutan Values koleksiyonunu kontrol etmelisiniz. Önce Cookies nesnesi yardımıyla yeniden oluşturmalısınız. Daha sonra Values koleksiyonunun Remove metodunu çağırmalısınız (Parametresine de silinecek subkeyin adını geçmelisiniz.). Sonra da bunu tarayıcıya yollarsanız işlem tamamlanmış olur.

string subkeyName;
subkeyName = "userName";
HttpCookie aCookie = Request.Cookies["userInfo"];
aCookie.Values.Remove(subkeyName);
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

Kısaca güvenliğine değinelim. Gerçi yazı içinde fark ettiyseniz bir-iki yerde biraz girdik bu konuya. Örneğin, kullanıcı bilgisayarından cookie'yi değiştirip size geri gönderebilir. Cookie üzerinde oynamalar olmuş olabilir. Çok fazla güvenli olmadığı için önemli bilgileri de tutmamanız gerektiğini bir kez daha hatırlatalım. Bahsedilen HtmlEncoding metodu da güvenlik konusu kapsamında düşünülebilir. Cookieler plaintext olarak gidip geldiğinden yolda(server-tarayıcı arasında) da değiştirilmiş olabilir. Bunun için SSL(Secure Sockets Layer) kullanılabilir. Kullanıcının makinasındayken SSL herhangi bir koruma sağlamaz ancak tarayıcı ile server arasında cookieyi şifreler ve cookie bu arada artık plaintext olarak değil, ciphertext olarak gidip gelir.

Son bir hususa daha değinmek istiyorum. Tarayıcılar cookieleri kabul etmeyebilirler. Eğer kullanıcı böyle bir düzenleme yaptıysa. Bu işlem sonrası, tarayıcıdan server'a herhangi bir bilgi de gitmez. Cookie yazdırılamayınca hata falan da almazsınız zaten. Ancak bunu anlamak için tek yol bir cookie yazıp okumaya çalışmak olacaktır. Son bir not: Cookies property'si size tarayıcının cookie alımına açık ya da kapalı olduğunu bildirmez. Sadece tarayıcının yapısında cookie alım özelliğinin bulunup bulunmadığını söyler.

Cookie konusu da kısaca böyle arkadaşlar. Maalesef yine State Management konusunu bitiremedim. :) Böylelikle ViewState ve Cookie'yi görmüş olduk. Diğer nesnelere de en yakın zamanda gireceğiz.

Görüşmek üzere..

Gürkan Alkan
İstanbul Üniversitesi Bilgisayar Mühendisliği

Hiç yorum yok: