Miłosz Orzeł

.net, js, html, arduino, java... no rants or clickbaits.

View State dla TextBox i innych kontrolek implementujących IPostBackDataHandler

Czytając oficjalny trening kit do egzaminu 70-515 natrafiłem na taki tekst: "With view state, data is stored within controls on a page. For example, if a user types an address into a TextBox and view state is enabled, the address will remain in the TextBox between requests.". Skoro takie zdania padają w zalecanym podręczniku, nic dziwnego, że łatwo pogubić się w tym jak ASP.NET Web Forms próbuje radzić sobie z naturalną dla HTTP bezstanowością... ;)

Kontrolka TextBox ze strony ASPX:

<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

Jest na stronie HTML renderowana jako tag input:

<input name="TextBox1" type="text" id="TextBox1" />

Skoro tak, to dla zachowania wartości kontrolki między requestami nie ma konieczności przechowywania wartości pola TextBox1 w ukrytym polu __VIEWSTATE. By się o tym przekonać stwórz prostą stronę zawierajacą kontrolki TextBox i Button:

...

<body>
    <form id="form1" runat="server">
    	<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    	<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" /
    </form>
</body>
</html>

i dodaj handler dla zdarzenia Click przycisku, którego jedynym zadaniem jest rozwijanie tekstu znajdującego się w kontolce TextBox1:

protected void Button1_Click(object sender, EventArgs e)
{
    TextBox1.Text += "X";
}

Następnie uruchom stronę i aktywuj narzędzie pozwalające na monitorowania komunikacji pomiędzy przeglądarką a serwerem. Interesuje nas badanie danych formularza przesyłanych na serwer przy postbacku... Jeśli używasz IE polecam program Fiddler, pod Firefoxem skorzystaj z dodatku Firebug. Możesz też użyć wbudowanego w ASP.NET mechanizmu Trace - w tym celu dopisz Trace="true" do dyrektywy @Page. Ja przeprowadzę test przy użyciu narzędzi deweloperskich dostarczonych wraz z przeglądarką Chrome (zakładka "Network").

Na poniższym screenie widać jakie dane formularza (request HTTP POST) zostały wysłane przy pierwszym naciśnięciu przycisku:

Dane formularza przy pierwszym postbacku

Tutaj widać dane z drugiego postbacka:

Dane formularza przy drugim postbacku

Jeśli porównasz dane z pierwszego i drugiego żądania, zobaczysz, że zmiana wartości TextBox1.Text nie wpływa na wartość pola __VIEWSTATE. Rozszerzanie tego pola byłoby marnotrastwem zasobów łącza skoro tekst jest wysyłany na serwer w osobnym polu o nazwie TextBox1.

Klasa System.Web.UI.WebControls.TextBox jest jedną z kilku klas implementującyh interfejs IPostBackDataHandler. Interfejs ten wymaga istnienia metody LoadPostData. Po zakończeniu incjalizacji strony (ale przed zdarzeniem Load), wykonywane jest ładowanie danych z View State (LoadViewState) a następnie (o ile kontrolka implementuje IPostBackDataHandler) z danych formulrza (LoadPostData). Właściwość Text kontolki typu TextBox może być zatem ustawiona nawet jeśli mechanizm View State jest wyłączony (poprzez ustawienie EnableViewState="false").

Czy nie można więc zupełnie zrezygnować z mechanizmu View State dla TextBoxa i podobnych kontrolek?

Nie. View State przydaje się np. wtedy gdy obsługiwane jest zdarzenie TextChanged (dla porównania wartości aktualnej i poprzedniej). Może mieć też zastosowanie gdy ustawiana jest inna właściwość kontrolki niż ta utożsamiana z wartością pola (np. ForeColor).

Niepewne działanie TextBox.MaxLength (użycie Fiddlera do modyfikacji żądania wysłanego przez IE)

Ustawienie właściwości MaxLength w kontrolce asp:TextBox pozwala na ograniczenie ilości znaków jakie użytkownik może wprowadzić w pole tekstowe. Ustawienie to działa jednak jedynie gdy właściwość TextMode kontrolki ustawiona jest na SingleLine lub Password (nie zadziała w przypadku opcji MultiLine). Dzieje się tak ponieważ w dwóch pierwszych przypadkach TextBox renderowany jest jako element HTML input type="text", który może posiadać atrybut maxlength informujący przeglądarkę o konieczności ograniczenia ilości znaków. Gdy ustawi się tryb wielowierszowy, TextBox renderowany jest jako element textarea (który nie obsługuje atrybutu maxlenght).

Korzystając z właściwości MaxLenght musisz pamiętać o pewnym problemie! Otóż ograniczenie długości tekstu działa jedynie w przeglądarce. Po stronie serwera długość tekstu nie jest sprawdzana. Jeśli nie zastosujesz zatem odpowiedniej walidacji, złośliwy użytkownik będzie miał możliwość wpisania dowolnie długiego tekstu. Wystarczy tylko odpowiednio spreparować POST.

Do modyfikacji żądania wysyłanego na serwer posłużymy się darmowym narzędziem Fiddler. Jest to proxy służące do monitorowania komunikacji klient-serwer, posiadające również możliwość modyfikacji wysyłanych przez przeglądarkę żądań.

Do testów (sprawdzałem na .NET Framework 3.5, IE7 i Fiddler 2.2.4.2 beta) użyjemy prostej strony zawierającej TextBox i Button:

<asp:TextBox ID="TextBox1" runat="server" MaxLength="5"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />

Jak widzisz maksymalna długość tekstu ograniczona jest (pozornie) do 5 znaków. Uruchom przygotowaną stronę i wypełnij pole tekstowe np. pięcioma literami "a".

Teraz uruchom Fiddlera (menu Narzędzia | Fiddler2) i ustaw automatyczne przerwanie (breakpoint) na requestach przeglądarki naciskając klawisz F11. W pasku stanu powinna pojawić się czerwona ikona z symbolem pauzy:

Po ustawieniu przerwań powróć na stronę testową i naciśnij przycisk by spowodować postback. POST towarzyszący naciśnięciu przycisku zostanie wyłapany przez Fiddlera (jeśli Fiddler nie przechwytuje ruchu przeczytaj uwagę na końcu tekstu). W oknie widocznym po lewej stronie kliknij w ostatni request (oznaczony czerwoną ikoną). Teraz możesz dowolnie zmodyfikować dane wysyłane na serwer.

W prawej części okna wybierz zakładkę "Inspectors". Następnie wybierz przycisk "WebForms" (przy pomocy tej opcji najłatwiej modyfikować pola formularzy). Czy widzisz w sekcji "Body" pole "TextBox1" z prowadzonym przez Ciebie pięcioliterowym tekstem? Dopisz do niego dowolne litery i naciśnij zielony przycisk "Run to Completion" by wznowić normalną komunikację przeglądarki z serwerem WWW.

Co stało się po przeładowaniu strony? Pole tekstowe posiada teraz tekst dłuższy od dopuszczalnego! Znaczy to, że infrastruktura ASP.NET nie sprawdza długości pola - po prostu aktualizuje właściwość Text:

* Istnieje prosty trik umożliwiający Fiddlerowi przechwytywanie ruchu na stronie z adresem localhost. Dodaj kropkę po słowie "localhost" i przeładuj stronę tak by jej adres wyglądał np. tak: http://localhost.:7913/Test