260 likes | 467 Views
ASP.net Form State. Jeremy Boyd, Senior Technical Lead - Intergen MSDN Regional Director – New Zealand. Todays Objectives. Discuss ViewState Used for supplemental state transmission Event Identification Hidden fields used to distinguish events
E N D
ASP.net Form State Jeremy Boyd, Senior Technical Lead - Intergen MSDN Regional Director – New Zealand
Todays Objectives • Discuss 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 Objectives • Alternative inter-page state propagation • Query string • Items collection
ViewState • To 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
Sample Page relying on ViewState <%@ Page Language="C#" %> <html> <scriptrunat="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> <formrunat="server"> <h2>ASP.NET accumulator page</h2> <inputsize="2"type="text"id="_op"runat="server"/> Sum:<spanid="_sum"runat="server">0</span> <p><inputtype="submit"value="Add"/></p> </form> </body></html> <%@ Page Language="C#" %>
Sample Page – HTML output <html> <body> <formname="_ctl0"method="post" action="accumulator.aspx"id="_ctl0"> <inputtype="hidden"name="__VIEWSTATE" value="dDwtMTE3NzEwNDc2Njs7PvcRil1nMNe70yha9afq+YEvj46N"/> <h2>ASP.NET accumulator page</h2> <inputname="_op"id="_op"type="text"size="2"/> Sum:<spanid="_sum"></span> <p> <inputtype=submitvalue="Add"/> </p> </form> </body> </html>
Decoding ViewState • ViewState is persisted as a tuple-based data structure and encoded as a base64 string dDwxMTgxOTcyOTQwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDU+Oz47bDx0PHA8cDxsPFR leHQ7PjtsPDE7Pj47Pjs7Pjs+Pjs+Pjs+coPvKE0gKu8gNg298/1rC07unJE= t<1181972940;t<;l<i<1>;>;l<t<;l<i<5>;>;l<t<p<p<l<Text;>;l<1;>>;>;;>;>>;>>;>r(M * 6kN
ViewStateDecoder • Available at • http://www.develop.com/devresources/
Disabling ViewState • ViewState can be disabled for individual controls, an entire page, or even an entire application <%@ Page Language="C#" EnableViewState="False" %> <html> <!-- ... --> <%@ Page Language="C#" EnableViewState="False" %> <%@ Page Language="C#" %> <html> <body> <formrunat="server"ID="Form1"> <inputtype="text"id="_op"runat="server"EnableViewState="false"/> <spanid="_span1"runat="server"EnableViewState="false"/> <p><inputtype="button"value="Enter"runat="server"/></p> </form> </body></html> <%@ Page Language="C#" %>
Protecting ViewState • ASP.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 Encryption • Must 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 SSL • For deployment on a WebFarm, you must specify the same validation key on all machines: • <machineKey validation='3DES'validationKey='7DFB068D0FC3D9E2E3C2BCA32DF9509DEFF71B8F44DF95578CCFCD2B6EA50475BEE615AF0E64072EC07A1D4720F11A5125947C592512BA72290812F911963CC8' />
Generating a Validation Key using System; using System.Text; using System.Security; using System.Security.Cryptography; class App { staticvoid Main(string[] argv) { int len = 48; if (argv.Length > 0) len = int.Parse(argv[0]); byte[] buff = newbyte[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); } }
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 with Multiple Events <%@ Page Language="C#" %> <html><scriptrunat="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><formrunat="server"> <h2>ASP.NET event page</h2> <asp:LinkButtonrunat="server"Text="Click Me!" OnClick="OnLinkButton"/><br/> <asp:DropDownListrunat="server"ID="_ddl"AutoPostBack="true" OnSelectedIndexChanged="OnIndexChanged"> <asp:ListItemSelected="True"Value="1"> item 1 </asp:ListItem> <asp:ListItemValue="2"> item 2 </asp:ListItem> <asp:ListItemValue="3"> item 3 </asp:ListItem> <asp:ListItemValue="4"> item 4 </asp:ListItem> </asp:DropDownList> <br/> <asp:Labelid="_message"runat="server"EnableViewState="false"/> </form></body></html>
Event Page Rendering <html><body> <formname="_ctl0"method="post"action="event.aspx"id="_ctl0"> <inputtype="hidden"name="__EVENTTARGET"value=""/> <inputtype="hidden"name="__EVENTARGUMENT"value=""/> <inputtype="hidden"name="__VIEWSTATE"value="..."/> <scriptlanguage="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> <ahref="javascript:__doPostBack('_ctl1','')">Click Me!</a> <br/> <selectname="_ddl"onchange="__doPostBack('_ddl','')" language="javascript"id="_ddl"> ...
Change notification events • Server-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 ViewState • ViewState can also be used as a means to store client-specific state publicclass PurchasePage : Page { privatevoid 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 State • Session 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...
Session State – Sample usage // Inside a page of the application protectedvoid 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 18 if (((int)Session["Age"]) >= 18) VoteButton.Enabled = true; else VoteButton.Enabled = false;
Cookies • Client-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
Using Cookies in ASP.net protectedvoid 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); } }
QueryString State • State 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 ?name=joe&age=21 http://turtle.net.nz/test.aspx?name=joe&age=21
QueryString Example Signup.aspx.cs privatevoid _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()); } ThankYou.aspx.cs privatevoid 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(); } Request["firstname"] Request["lastname"] Request["zipcode"]
Context.Items State • The 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
Context.Items State Example Signup.aspx.cs privatevoid _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"); } Context.Items["firstname"] Context.Items["lastname"] Context.Items["zipcode"] ThankYou.aspx.cs privatevoid 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(); } Context.Items["firstname"] Context.Items["lastname"] Context.Items["zipcode"]
Summary • ASP.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