Her Yönüyle ViewState Kavramı
| Durum Yönetimine Genel Bakış |
| -View State -Control State -Hidden Form Fields -Cookies -Query String -Application State -Session State |
ViewState soyut olarak nedir?
ViewState (Görüntü Durumu), verileri saklamak için default olarak kullanılan tekniktir. Sayfa post back olduğunda ve geri geldiğinde aynı sayfadaki değerlerin saklanması işlemini ViewState yapar. Kısacası sayfanın tarayıcıdan dönerkenki haline ulaşabilmemizi ve sayfa geri gönderilirken otomatikman tekrar bu bilgilerle gitmesini sağlar. Dolayısıyla sayfalar arası veri taşımaz. ViewState 2 türlü kullanılır. İlki ASP.NET tarafından otomatik olarak yönetilmesidir. Kapatılmadığı sürece arkada çalışmaya devam eder. İkincisi ise kod kısmında koleksiyon olarak kullanılmasıdır. Peki ViewState nasıl çalışır? ASP.NET arka tarafta gizli bir input kontrolü yaratır. Bu input kontrolünün içinde, sayfadaki istenilen kontrollerin bazı propertylerini base64 koduna çevirerek saklar. Bu gizlenmiş ViewState kontrolü sayfayla birlikte yaşar ve seyahat eder. Dolayısıyla eğer dikkatli ve kontrollü olarak kullanılmassa, View State kontrolümüz gittikçe şişer ve ciddi performans sıkıntılarına sebep olur. Üstelik aspx sayfamızın içinde bulunan bir kontrol olduğu için hem post back hem de request süresini uzatır.
Bu kadar teorik bilginin ardından View State kavramına biraz da pratik olarak bakalım
ViewState somut olarak nedir?
Basit bir web uygulaması açalım. İçersinde hiç bir değişiklik yapmadan debug edip, kaynak kodunu açtığımızda şu html çıktısı ile karşılaşırız;
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head><title> Untitled Page </title></head> <body> <form name="form1" method="post" action="Default.aspx" id="form1"> <div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGRKSpuLejDHB6P+RKbZyCauWTRAqA==" /> </div> <div> </div> </form> </body> </html> |
Şimdide kısa bir örnekle ViewState nerelerde kullanılabilir ona bakalım. Öncelikle yeni web projesi açıp içine bir adet DropDownList ve bir adette Button nesnesi ekleyelim. Senaryomuz gereği, Button nesnemize basıldığında DropDownList içindeki nesneler ters sırayla yazılmalıdır (yada kısaca Reverse() metodu çalıştırılmalıdır);
| protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { DropDownList1.Items.Add("CSharp"); DropDownList1.Items.Add("Nedir"); DropDownList1.Items.Add(".com"); } } protected void btnSirala_Click(object sender, EventArgs e) { ArrayList ary = new ArrayList(); ary.AddRange(DropDownList1.Items); ary.Reverse(); DropDownList1.Items.Clear(); foreach (object obj in ary) { DropDownList1.Items.Add(obj.ToString()); } } |
| <asp:DropDownList ID="DropDownList1" runat="server" EnableViewState="false" > |
Şimdide kod tarafında ViewState ile nasıl oynayabiliriz ona bakalım. Buradaki senaryomuzda Bir ListBox kontrolü içine istediğimiz kelimeleri ekleyip çıkartalım:
| protected void Page_Load(object sender, EventArgs e) { } protected void btnEkle_Click(object sender, EventArgs e) { ViewState.Add(txtEkle.Text.ToString(), txtEkle.Text); //ViewState kontrolümüze dışarıdan bir nesne eklenmeye çalışıldığı zaman aynı Hashtable gibi çalışır. Bizden bir 'Key' birde 'Value' değeri ister. Hashtable'dan farklı olarak 'Key' değerini string olarak alır.'Value' değerini ise object olarak aldıktan sonra StateItem tipine cast eder. Artık eklendikten sonra bu verilere, istenildiği kadar post back yapılsın, ne zaman arzu edilirse 'Key' değeri üzerinden ulaşılabilir. Tazele(); } protected void btnCikar_Click(object sender, EventArgs e) { ViewState.Remove(txtCikar.Text.ToString()); //Remove metodu ise sadece key değeri üzerinden işlem yapmaktadır. Tazele(); } private void Tazele() { //ListBox1.Items.Clear(); Normalde bu metodu yazmadığımız takdirde ListBox nesnemiz içeriğini ViewState vasıtasıyla taşımaya devam eder. Bu da verilerin birikmesine sebep olmaktadır. Dolayısıyla html tarafında ListBox'ın ViewState özelliğini kapatarak hem bu satırı yazmaktan kurtuluruz, hem de ViewState'i daha az yorarız. Burdan da şu anlaşılmaktadır; Biz kod tarafında herhangi bir kontrolün Items.Clear() metodunu çalıştırdığımız zaman ViewState'ten o kontrole ait nesneler kaldırılmaktadır. foreach (StateItem sti in ViewState.Values) { ListBox1.Items.Add(sti.Value.ToString()); } } |
| ASP.NET bizim girdiğimiz StateItem nesneleri ile kendi yarattığı StateItem nesnelerini ViewState içerisinde ayrı ayrı saklamaktadır. Dolayısıyla kod tarafında ViewState'ten sadece kendimizin 'Key' değeri girerek oluşturduğu verilere ulaşabilirz. |
| public class Control : IComponent, IDisposable, IParserAccessor,... { ... protected virtual StateBag ViewState { get; } ... } |
| public sealed class StateBag : IStateManager, IDictionary, ICollection,
IEnumerable { public StateBag(); public StateBag(bool ignoreCase); public int Count { get; } public ICollection Keys { get; } public ICollection Values { get; } public object this[string key] { get; set; } public StateItem Add(string key, object value); public void Clear(); public IDictionaryEnumerator GetEnumerator(); public bool IsItemDirty(string key); public void Remove(string key); public void SetDirty(bool dirty); public void SetItemDirty(string key, bool dirty); } |
| public class Hashtable : IDictionary, ICollection, IEnumerable ... public class SortedList : IDictionary, ICollection, IEnumerable ... |
ViewState ve Hashtable'ın kullanımlarını karşılaştırırsak:
| ViewState Kullanımı | Hashtable Kullanımı |
| for (int i = 0; i < 10; i++) { ViewState[i.ToString()] = i; } |
Hashtable ht = new Hashtable(); for (int i = 0; i < 10; i++) { ht[i] = i; } |
1- ViewState 'Key' değer olarak string alırken, Hashtable object almaktadır.
2- ViewState 'Value' değerini StateItem, Hashtable ise object tipine çevirir.
3- Hashtable 'Key' değerlerini unique olarak istemektedir ve aynı olursa hata vermektedir. ViewState ise aynı 'Key' değeri olduğu durumlarda en son ekleneni alır.
4- Hashtable bir sınıf olduğundan ilk başta örneklenmesi gerekir. Fakat View State virtual olmasından dolayı bizim tarafımızdan örneklenemez. Eğer böyle bir yapıya sahip olsaydı sayfa her post back olduğunda içindeki değerler sıfırlanırdı. Sonuç olarak View State, kapalı veya açık olsun, her sayfada mutlaka debug sırasında bir adet örneklenir ve html kodları arasına yerleştirilir. Daima bir tane olmasının sebebi ise bazı kontrollerin ViewState'e ihtiyaç duymasıdır.
ViewState'in yüklenmesi ve kaydedilmesi
ASP.NET, sayfadan gelen ViewState'in içeriğindeki veriyi almak için LoadViewState, ViewState'e yeniden düzenlenmiş veriyi kaydetmek içinse SaveViewState metodunu kullanmaktadır. Bu iki metod framework tarafından otomatikman kullanıldığı gibi bizim tarafımızdan da istediğimiz yerde yazılabilir. ASP.NET bu metodları yaşam döngüsünde şu aralarda çalıştırmaktadır:
| -Initialize -TrackViewState() :'Bu metoda az aşağıda değinilmektedir.' -LoadViewState() -Process postback data -Load -Send postback change notifications -Handle postback events -Prerender -SaveViewState() -Render -Dispose -Unload |
| protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { Label1.Text = "Çarşı .Net'e karşı"; } protected void Button2_Click(object sender, EventArgs e) { Label1.BackColor = Color.Red; } protected void Button3_Click(object sender, EventArgs e) { Label1.CssClass = "style1"; } |
Başlangıçta sayfamız bu haldedir:
Button1’e basıldığında:
Button2’ye basıldığında:
Button3’e basıldığında:
| <script type="text/javascript"> var size = document.getElementById("__VIEWSTATE").value.length; document.write(size+"bytes"); </script> |
Sayfamıza bir DropDownList ve iki adet Button nesnesi ekleyelim. Button nesnelerinin görevi ViewState'e ekliyeceğimiz nesnenin dirty özelliğine true veya false atamaktır.
| protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { ViewState["key"] = "value"; } } protected void Button1_Click(object sender, EventArgs e) { ViewState.SetItemDirty("key", true); DropDownList1.Items.Clear(); foreach (StateItem sti in ViewState.Values) { DropDownList1.Items.Add(sti.Value.ToString()); } } protected void Button2_Click(object sender, EventArgs e) { ViewState.SetItemDirty("key", false); DropDownList1.Items.Clear(); foreach (StateItem sti in ViewState.Values) { DropDownList1.Items.Add(sti.Value.ToString()); } } |
Buradan şu sonuç çıkmaktadır. Biz ViewState["key"]=value; kodunu yazdığımız anda ViewState’e bu veri eklenmek için SaveViewState metodunu bekler ve ’dirty’ özelliği ne olursa olsun eklenir. Fakat eğer bu veri ViewState’e sadece bir kez ekleniyorsa, (Yukarıdaki !Page.IsPostBack metodu içerisinde olduğu anlar gibi) ’dirty’ özelliği, true olana kadar taşınmaya devam eder ama false ise son bir kez taşınıp daha sonrasında silinir. ViewState.Remove() metodu ise hiç ViewState’e eklemeden siler.
-ChildControls ve TrackViewState: Eğer sayfaya bir kontrolü dinamik olarak ekliyorsak şu duruma dikkat etmemiz gerekir. Kontroller dinamik olarak eklendiği zaman TrackViewState metodu, kontrolü sadece formun Add() metodu çalıştırıldıktan sonra etkilemeye başlar. Dolayısıyla Add metodu, dolaylı olarak da TrackViewState metodu çalıştırılmadan önce yazılan veriler kontrole eklenir fakat ViewState'e eklenmez:
| protected void Page_Load(object sender, EventArgs e) { DropDownList drpList = new DropDownList(); if (!Page.IsPostBack) { for (int i = 0; i < 10; i++) { drpList.Items.Add(i.ToString()); } } this.form1.Controls.Add(drpList);//Bu durumda TrackViewState metodu burada çalıştırılır. Dolayısıyla veriler ViewState'e eklenmez sadece kontrole bir kereye mahsus olmak üzere eklenir. Postback olduğunda DropDown boş gelir. } //Bunun yerine Kontrolü önce forma ekleyip sonra verileri bağlamak daha sağlıklı olur. protected void Page_Load(object sender, EventArgs e) { DropDownList drpList = new DropDownList(); this.form1.Controls.Add(drpList);//Artık TrackViewState metodu buradan itibaren çalışmaya başlıyacaktır ve veriler hem DropDown'a hemde ViewState'e eklenecektir. if (!Page.IsPostBack) { for (int i = 0; i < 10; i++) { drpList.Items.Add(i.ToString()); } } } |
ViewState bilindiği üzere değerleri base64 kodlarına çevirmektedir. Şunu söylemek gerekir ki base64 kodları şifreli değildir ve kırılması çok basittir. ViewState'in diğer kötü yanlarından biri de bir html nesnesi olmasından dolayı herkes tarafından görülebilmesidir. Bu iki etken bir araya gelince veri güvenliği diye bir kavram kalmamaktadır. İşte değerli verilerimizi ViewState ile taşıdığımız anlarda yardımımıza ViewStateMac koşmaktadır.
ViewStateMac: EnableViewState özelliği ile EnableViewStateMac özelliği karıştırılmamalıdır. ViewStateMac(ViewState Machine Authentication Check) güvenlik amaçlı konulmuş bir özelliktir. ViewState üzerindeki orjinal datanın bozulmasına yönelik herhangi bir saldırı olup olmadığını kontrol eder ve verileri base64 yerine hash koduna çevirir. Default olarak açıktır ve ViewState özelliğinin olduğu heryerde bu özellikde vardır. ViewStateMac, sayfa tarayıcıya giderken verileri hash kodundan geçirir ve post back olduğunda gelen verilerle daha önceden oluşturduğu hash kodunu karşılaştırır. Bunun için küçük bir test yapalım. Boş bir web sayfası açıp <%@ Page %> kısmına EnableViewStateMac="false" özelliğini ekleyelim. Ardından debug edip sayfanın kaynak koduna bakalım. ViewState'in value değeri şu 24 haneli string çıkmıştır: "/wEPDwUJNzgzNDMwNTMzZGQ=" . Şimdide ViewStateMac'i aktif hale getirelim. Bu sefer value değerimiz 52 hanelidir: "/wEPDwUJNzgzNDMwNTMzZGTOg2Hi9CvgCScuhB5ASVZPIeJCsw==" . Bunun sebebi ViewStateMac açıldığından değerlerin hash koduna çevrilmesidir. Bu işlem verilerin değiştirilmesini çok zorlaştırır fakat diğer yandan da sayfanın boyutunu da arttırmaktadır.
ASP.NET’in kullandığı hash kodu standartlarına bakmadan önce web.config dosyasını kısaca bir hatırlayalım:
| machine.config aynı web.config dosyası gibi xml tabanlı bir dosya olup o
sunucuda bulunan tüm siteleri etkiler. ASP.NET'te özellikler aşağıdaki
hiyerarşiye göre belirlenir: 1- Makine bazında: machine.config 2- Site bazında: web.config 3- Sayfa bazında: <%@ Page %> 4- Kontrol bazında. Fakat üstünlük bakımından ise bu sıra tam tersidir. ASP.NET, özellikleri önce <%@ Page %> takısından kontrol etmeye başlar. Yani <%@ Page %> web.config'den, web.config'de machine.config'den daha önceliklilidir. *machine.config dosyasına ulaşmak için ise şu path izlenir: WINDOWS\Microsoft.NET\Framework\Framework sürümü\CONFIG\machine.config |
| <configuration> <system.web> <machineKey validation="SHA1" /> |
-AES (Advanced Encryption Standart): 128, 192 veya 256 bit
-3DES (Triple Data Encryption Standart): 56x3 bit
-SHA1 (Secured Hashing Algrotihm 1): 160 bit
-MD5 (Message-Digest algorithm 5): 128 bit
Anahtar büyüklüğündeki Bit boyutunun artmasıyla doğru orantılı olarak kodun kırılma zorluğu da artar.
SHA1 ve MD5: Bu iki algoritma şifreleme yapmazlar. Şifreli olmamaları yanlış anlaşılmamalıdır. Bu tamamen açık oldukları anlamına gelmez, sadece ileri düzey hash kodlarından geçirilmediğini gösterir. Fakat bugün brute force saldırılarıyla kırılabilmektedirler. SHA1, MD5'a göre daha fazla yer tutar ve daha yavaş çalışır fakat daha güvenlidir.
- Tamper Proofing (Kırılmaya dayanıklı): ViewState'imizi eğer orta derecede güvenlik altına almak istiyorsak öncelikle EnableViewStateMac özelliğine true atamamız gerekir. Daha sonrasında ise machine.config dosyasına şu özellikler yazılmalıdır:
| <configuration> <system.web> <machineKey validationKey="AutoGenerate,IsolateApps" validation="SHA1" /> 'veya MD5' |
IsolateApps ise sunucuda bulunan her bir site için farklı bir id belirleyerek bu anahtar değerlerini birbirinden soyutlar. validationKey ile aynı şekilde çalışan bir de decryptionKey vardır. Bu özellik ise şifreleme yapılırken kontrol etmek için bir anahtar değer üretir.
AES ve 3DES: Bu algoritmalar ise verileri şifrelerler. Kırılmaları çok zordur ve maliyetlidir. AES, 3DES'e göre çok yeni bir standarttır. Dolayısıyla 2.0 ile birlikte eklenmiştir ve 3DES'e göre çok daha güvenlidir. 3DES'in yaptığı iş verileri 56 bitlik anahtar büyüklüğündeki hash kodundan 3 kere geçirmesidir. AES ise 3 ayrı anahtar değerine sahiptir.
- Encryption (Şifreleme): ViewState'in şifrelenmesi için bir kaç ayar vardır. İlk başta ViewStateMac açılmalıdır. Daha sonrasında web.config'e veya <%@ PAGE &>'e şu özellik eklenir:
| ... <system.web> <pages viewStateEncryptionMode="Always"></pages> ... |
| Page.RegisterRequiresViewStateEncryption(); |
| <configuration> <system.web> <machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="AES" 'veya 3DES' decryption="Auto 'Bu özellik şifrelenmiş veriyi hangi standartla çözüceğimizi belirtmemizi sağlar. Özel bir durum olmadıkça Auto olarak ayarlanmalıdır. '/> |
dipnot: Eğer siteniz bir web farm vasıtasıyla çalışmıyor ise bu özellikleri kullanınız.
View State'ten faydalanmamak
Her ne kadar bir çok yerden kolaylık sağlasada, bazı durumlarda, özellikle boyutunun çok büyüdüğü anlarda ViewState'in kapatılması gerekebilir. ViewState şu yöntemler kullanılarak kapatılabilir.
| 1- Kontrol bazında; |
| <asp:TextBox ID="TextBox1" runat="server" EnableViewState="false"></asp:TextBox> |
| 2- Sayfa bazında; |
| <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" EnableViewState="false" %> |
| 3- Tüm sitede ViewState özelliğinin kapatılması için ise web.config dosyasının içine şu kod yazılır; |
| ... <system.web> <pages enableViewState="false"></pages> ... |
| protected override void SavePageStateToPersistenceMedium(object viewState) { } protected override object LoadPageStateFromPersistenceMedium() { return null; } |
ViewState'in çok büyüdüğü anlarda karşılaşılan sorunlardan biri de bazı firewall ve proxy'lerin bu olgunlaşmış ViewState'leri kabul etmemesidir. Bu zorluğun atlatılması kolaydır. Tek yapmamız gereken web.config dosyası içine şu satırı yazmaktır:
| <configuration> <system.web> <pages maxPageStateFieldLength=""></pages> |
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head><title> Untitled Page </title></head> <body> <form name="form1" method="post" action="Default.aspx" id="form1"> <div> <input type="hidden" name="__VIEWSTATEFIELDCOUNT" id="__VIEWSTATEFIELDCOUNT" value="3" /> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTYxMTc1NjI2MA9kFgICAw9kFgICAQ8QZA8WCm YCAQICAgMCBAIFAgYCBwIIAgkWChAFATEFATFnEAUBMgUBMmcQBQEz" /> <input type="hidden" name="__VIEWSTATE1" id="__VIEWSTATE1" value="BQEzZxAFATQFATRnEAUBNQUBNWcQBQE2BQE2ZxAFATcFATdnEA UBOAUBOGcQBQE5BQE5ZxAFAjEwBQIxMGdkZGT//mlq/vcPuLOi" /> <input type="hidden" name="__VIEWSTATE2" id="__VIEWSTATE2" value="A2O9kXAuaxuGhw==" /> </div> <div> <select name="DropDownList1" id="DropDownList1"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> <option value="6">6</option> <option value="7">7</option> <option value="8">8</option> <option value="9">9</option> <option value="10">10</option> </select> </div> ... |
Bu makale umarım size çok faydalı olmuştur ve ViewState ile ilgili soru işaretlerini minimuma indirmiştir. Herkese bol ViewState’li siteler.
Serkan YAZICIOĞLU
serkanyazicioglu@gmail.com
Kaynaklar: http://msdn2.microsoft.com/en-us/library/ms998288.aspx
http://weblogs.asp.net/ngur/archive/2004/03/08/85876.aspx
http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx
Makale:
Her Yönüyle ViewState Kavramı ASP.NET Serkan Yazıcıoğlu
Her Yönüyle ViewState Kavramı ASP.NET Serkan Yazıcıoğlu
Hiç yorum yok:
Yorum Gönder