ASP.net Form State Jeremy Boyd, Senior Technical Lead - Intergen MSDN Regional Director – New...

Preview:

Citation preview

ASP.net Form State

Jeremy Boyd,Senior Technical Lead - IntergenMSDN Regional Director – New Zealand

Todays ObjectivesDiscuss ViewState– Used for supplemental state transmission

Event Identification– Hidden fields used to distinguish events– ViewState used to trigger change notification

events

Per-client state– ViewState– Session– Cookies

Todays ObjectivesAlternative inter-page state propagation– Query string– Items collection

ViewStateTo complete the control-based model, additional state must be transferred with each post back– Many HTML elements do not have their values sent in

a POST• table, span, div, p, li, etc.

– If the value of one of these elements is changed in a server-side control, it should retain that value across multiple requests

– Additional hidden input element is generated with each Page rendering called __VIEWSTATE

– Contains a base64-encoded string with supplemental state

<%@ Page Language="C#" %><html><script runat="server">protected void Page_Load(object src, EventArgs e){ if (IsPostBack) { int op1 = int.Parse(_op.Value); int op2 = int.Parse(_sum.InnerText); _sum.InnerText = (op1+op2).ToString(); }}</script><body> <form runat="server"> <h2>ASP.NET accumulator page</h2> <input size="2" type="text" id="_op" runat="server"/> Sum:<span id="_sum" runat="server">0</span> <p><input type="submit" value="Add" /></p> </form></body></html>

<%@ Page Language="C#" %>

Sample Page relying on ViewState

<html>

<body> <form name="_ctl0" method="post" action="accumulator.aspx" id="_ctl0"> <input type="hidden" name="__VIEWSTATE" value="dDwtMTE3NzEwNDc2Njs7PvcRil1nMNe70yha9afq+YEvj46N" />

<h2>ASP.NET accumulator page</h2> <input name="_op" id="_op" type="text" size="2" /> Sum:<span id="_sum"></span> <p> <input type=submit value="Add" /> </p> </form></body></html>

Sample Page – HTML output

Decoding ViewStateViewState is persisted as a tuple-based data structure and encoded as a base64 string

t<1181972940;t<;l<i<1>;>;l<t<;l<i<5>;>;l<t<p<p<l<Text;>;l<1;>>;>;;>;>>;>>;>r(M * 6kN

dDwxMTgxOTcyOTQwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDU+Oz47bDx0PHA8cDxsPFRleHQ7PjtsPDE7Pj47Pjs7Pjs+Pjs+Pjs+coPvKE0gKu8gNg298/1rC07unJE=

ViewStateDecoderAvailable at – http://www.develop.com/devresources/

Disabling ViewStateViewState can be disabled for individual controls, an entire page, or even an entire application

<%@ Page Language="C#" EnableViewState="False" %><html><!-- ... -->

<%@ Page Language="C#" %><html><body> <form runat="server" ID="Form1"> <input type="text" id="_op" runat="server" EnableViewState="false" /> <span id="_span1" runat="server" EnableViewState="false" /> <p><input type="button" value="Enter" runat="server" /></p> </form></body></html>

<%@ Page Language="C#" %>

<%@ Page Language="C#" EnableViewState="False" %>

Protecting ViewStateASP.NET provides two mechanisms for protecting ViewState– Tamper-proofing using a hashcode (SHA1 or MD5)– Encryption using Triple DES symmetric algorithm

(3DES)

Tamper-proofing can be enabled at the Page level (on by default)– <%@ Page enableViewStateMac='true' %>

Or at the application or machine level– <!-- in web.config or machine.config -->– <pages enableViewStateMac='true' />

ViewState EncryptionMust be enabled at the machine level:– <machineKey validation='3DES' />

Significantly increases the size of ViewState - use with caution!No need to encrypt if already using SSLFor deployment on a WebFarm, you must specify the same validation key on all machines:– <machineKey validation='3DES'

validationKey='7DFB068D0FC3D9E2E3C2BCA32DF9509DEFF71B8F44DF95578CCFCD2B6EA50475BEE615AF0E64072EC07A1D4720F11A5125947C592512BA72290812F911963CC8' />

using System;using System.Text;using System.Security;using System.Security.Cryptography;

class App { static void Main(string[] argv) { int len = 48; if (argv.Length > 0) len = int.Parse(argv[0]);

byte[] buff = new byte[len/2]; RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); rng.GetBytes(buff); StringBuilder sb = new StringBuilder(len); for (int i=0; i<buff.Length; i++) sb.Append(string.Format("{0:X2}", buff[i]));

Console.WriteLine(sb); }}

Generating a Validation Key

Identifying server-side events

Server-side events are issued during a POST request– Not obvious which controls fired events with a

generic POST• Was a select box changed?• Was button 1 pressed?• Was button 2 pressed?

– ASP.NET propagates additional event information• Hidden field __EVENTTARGET stores the control id that

issued the event• Hidden field __EVENTARGUMENT stores any parameters to

the event

<%@ Page Language="C#" %><html><script runat="server">protected void OnLinkButton(object src, EventArgs e){ _message.Text = "You clicked the link button!"; }

protected void OnIndexChanged(object src, EventArgs e) { _message.Text = string.Format("You changed the index to {0}!", _ddl.SelectedItem.Value);}</script><body><form runat="server"> <h2>ASP.NET event page</h2> <asp:LinkButton runat="server" Text="Click Me!" OnClick="OnLinkButton" /> <br/> <asp:DropDownList runat="server" ID="_ddl" AutoPostBack="true" OnSelectedIndexChanged="OnIndexChanged"> <asp:ListItem Selected="True" Value="1"> item 1 </asp:ListItem> <asp:ListItem Value="2"> item 2 </asp:ListItem> <asp:ListItem Value="3"> item 3 </asp:ListItem> <asp:ListItem Value="4"> item 4 </asp:ListItem> </asp:DropDownList> <br/> <asp:Label id="_message" runat="server" EnableViewState="false" /></form></body></html>

Page with Multiple Events

<html><body> <form name="_ctl0" method="post" action="event.aspx" id="_ctl0"><input type="hidden" name="__EVENTTARGET" value="" /><input type="hidden" name="__EVENTARGUMENT" value="" /><input type="hidden" name="__VIEWSTATE" value="..." /><script language="javascript">function __doPostBack(eventTarget, eventArgument) { var theform; if (window.navigator.appName.toLowerCase().indexOf("netscape") > -1){ theform = document.forms["_ctl0"]; } else { theform = document._ctl0; } theform.__EVENTTARGET.value = eventTarget.split("$").join(":"); theform.__EVENTARGUMENT.value = eventArgument; theform.submit();}</script> <h2>ASP.NET event page</h2> <a href="javascript:__doPostBack('_ctl1','')">Click Me!</a> <br/> <select name="_ddl" onchange="__doPostBack('_ddl','')" language="javascript" id="_ddl"> ...

Event Page Rendering

Change notification eventsServer-side controls that issue change notification events rely on ViewState– Prior value stored in ViewState– During subsequent POST current value is

compared with value cached in ViewState and change notification issued if different

Corollary: do not disable ViewState on controls whose server-side change events you are handling

Explicit use of ViewStateViewState can also be used as a means to store client-specific state

public class PurchasePage : Page{ private void Page_Load(object sender, EventArgs e) { ArrayList cart = (ArrayList)ViewState["Cart"]; if (cart == null) { cart = new ArrayList(); ViewState["Cart"] = cart; } // Use items stored in ArrayList }

Session StateSession state is used to store individual data for a user during page interaction– Session state is scoped by a single client session, and

is tagged with a unique (and hard to guess) session ID– The session ID is transmitted between the client and

server using cookies (or munged URLs if cookieless mode is enabled)

– Accessed through the Session property of a page, which references the current HttpSession object provided by the HTTP runtime

More on session and scalability later...

// Inside a page of the applicationprotected void Submit_Click(Object sender, EventArgs e) { Session["Age"] = AgeField.Value; // save age user entered // ... }

// Inside another page of the application // only let user vote if he/she is over 18if (((int)Session["Age"]) >= 18) VoteButton.Enabled = true;else VoteButton.Enabled = false;

Session State – Sample usage

CookiesClient-side cookies can be used to store user preferences/information– Server requests client to set cookie in response– Client sends cookie values in subsequent requests– Cookies may be persisted if the Expires property– Browsers limit cookie data -- only 4096 bytes guaranteed– Clients may disable cookies

Cookie interaction controlled with two collections:– Request.Cookies - cookies sent by client– Response.Cookies - cookies you are requesting client to

set

protected void Page_Load(object sender, EventArgs e) { if (Request.Cookies["Age"] == null) Response.Cookies.Add(new HttpCookie("Age", "21")); else { // use existing cookie value... int age = int.Parse(Request.Cookies["Age"].Value); }}

Using Cookies in ASP.net

QueryString StateState can be passed between pages by appending a query string to the URL– Must be passed as name/value pairs– Restricted to URL compatible strings

• Must use % to represent restricted characters as encoded (including /.#?;:$,+@&={}|\^[]')

– Indicate query string with '?' character– Delimit name/value pairs with '&' character– Access query string values through the

indexer in HttpRequest

http://turtle.net.nz/test.aspx?name=joe&age=21

?name=joe&age=21

private void _signupButton_Click(object sender, System.EventArgs e){ StringBuilder url = new StringBuilder(); // prepare query string url.Append("ThankYou.aspx?firstname="); url.Append(_firstname.Text); url.Append("&lastname="); url.Append(_lastname.Text); url.Append("&zipcode="); url.Append(_zipcode.Text); Response.Redirect(url.ToString()); }

private void Page_Load(object sender, System.EventArgs e){ StringBuilder msg = new StringBuilder(); msg.Append("<b>Registered user:</b> "); msg.Append(Request["firstname"]); msg.Append(" "); msg.Append(Request["lastname"]); msg.Append("<br/> <b>location=</b>"); msg.Append(Request["zipcode"]); _summary.InnerHtml = msg.ToString();}

Signup.aspx.cs

ThankYou.aspx.cs

Request["firstname"]

Request["lastname"]

Request["zipcode"]

QueryString Example

Context.Items StateThe Items collection of HttpContext can be used to store per-request state– Useful when passing data between elements

in the pipeline (like modules)– Can be used to pass data between pages

when using Server.Transfer

private void _signupButton_Click(object sender, System.EventArgs e){ Context.Items["firstname"] = _firstname.Text; Context.Items["lastname"] = _lastname.Text; Context.Items["zipcode"] = _zipcode.Text; Server.Transfer("ThankYou.aspx"); }

private void Page_Load(object sender, System.EventArgs e){ StringBuilder msg = new StringBuilder(); msg.Append("<b>Registered user:</b> "); msg.Append(Context.Items["firstname"]); msg.Append(" "); msg.Append(Context.Items["lastname"]); msg.Append("<br/> <b>location=</b>"); msg.Append(Context.Items["zipcode"]); _summary.InnerHtml = msg.ToString();}

Signup.aspx.cs

ThankYou.aspx.cs

Context.Items["firstname"]Context.Items["lastname"]Context.Items["zipcode"]

Context.Items["firstname"]

Context.Items["lastname"]

Context.Items["zipcode"]

Context.Items State Example

SummaryASP.NET developers must be aware of the various types of state forms use– ViewState is used for controls whose contents does not

propagate with traditional POST bodies– Change notifications managed via ViewState– Server-side events use supplemental hidden fields

Several types are available for use– ViewState - client-specific for POST requests to same page– Session state - client-specific tied to browser session– Cookie state - client specific string pairs– Query string state - for passing data between pages– Items collection - for transferring data between pages

Recommended