Pains of the Postback

Making Postback Simple and Painless

CoreCoder

LANGUAGES: C# | VB.NET

ASP.NET VERSIONS: 2.0

 

Pains of the Postback

Making Postback Simple and Painless

 

By Dino Esposito

 

The postback mechanism is an essential part of ASP.NET. Simply put, it consists of posting form data to the same server page that generated the present page with which the user is working. Once the contents of the present page are posted, a new page object is created and the view state information is used to restore the call context that is, the same state of controls existing when the posting page was last generated on the server.

 

After the page has been initialized and posted values have been taken into account, it s about time that some server-side events occur. There are two main types of events:

  • The first type of event signals that certain controls had the state changed over the postback.
  • The second type of event executes server code in response to the client action that caused the post.

 

The postback event involves some action on the client and the server and sets some rules that, if not fulfilled, may raise exceptions and provide unexpected behavior. In this article, I ll explore some of the issues and common actions related to postback, especially on the client side. In particular, I ll examine the impact of JavaScript-driven page changes on the postback mechanism, how to inject custom code in the submit process, and the role of the newest __EVENTVALIDATION hidden field.

 

Modifying Input Fields via Script

The growing use of script code in Web pages, and the growing level of standard Document Object Models (DOM) supported by browsers, makes it possible, and sometimes worthwhile, to use client script code to complete or modify the structure of the page. Let s consider a simple scenario: editing the contents of a TextBox using JavaScript. The following code shows the involved ASP.NET page elements:

 

 Text="Default value" />

 onclick="SetValue()" />

 

The JavaScript code to use is shown here:

 

 

You execute the page and click the Set Value button to edit the value of the input field programmatically. Next, you click the submit button to post the current form to the server. When a submit action occurs, the browser correctly picks up the current value in the TextBox and sends it over the wire to the server.

 

Inside the page lifecycle, the posted value is processed and used to update the state of the server-side counterpart of the TextBox. When the page has completed, the TextBox is configured to have the previously scripted value as its default value on the client. This behavior is not surprising at all, and applies to text input fields as well as hidden fields.

 

Let s now consider a slightly different scenario in which a brand new TextBox is created on the client that extends the page s DOM.

 

Creating New Input Fields via Script

To create a new input text field, you run some JavaScript code and insert raw HTML in the current page s object model. Here s how to do it:

 

var markup = "";

document.getElementById("placeHolder").innerHTML += markup;

 

The idea is that you prepare a string variable with the required markup, then programmatically associate it with the inner HTML of a placeholder tag; for example, a

tag found in the original ASP.NET page:

 

 

As Figure 1 shows, when the preceding code runs in a client page a new TextBox is added to the page and is made ready for editing.

 


Figure 1: Adding a new TextBox programmatically on the client.

 

Immediately after creating the new TextBox, you can also retrieve a reference and script it:

 

var newTextBox = document.getElementById("dynTextBox");

newTextBox.value = "Just added";

 

What if you click a submit button now and post back? Interestingly enough, here s the body of the HTTP packet being sent over the wire:

 

__VIEWSTATE=%2Fw...jz&

TextBox1=New+value&

Button2=Post+back...&

dynTextBox=Just+added&

__EVENTVALIDATION=%2Fw...Ykv

 

As you can see, the newly created TextBox is regularly part of the post and retains its displayed value. However, when the page is re-rendered to the client, the newly added TextBox has disappeared. What s up?

 

Quite simply, ASP.NET attempts to find a match between the ID of each posted element and server control. TextBox1, the sample TextBox we programmatically updated in the previous example, is part of the ASP.NET page, meaning that a reference to it is automatically created in the page initialization and easily found and updated with posted data.

 

The same doesn t happen for dynTextBox, which is completely unknown on the ASP.NET side. Hence, ASP.NET ignores the posted data and still renders all and only the controls originally declared in the .aspx source file.

 

At this point, though, you might object that if I m correct here it would suffice to write some server code to programmatically create a dynTextBox TextBox control during Page_Load to have the dynamically created input field become officially part of the page and survive across postbacks. Let s try it out.

 

You could retrieve programmatically the list of IDs with an entry in the Request.Form collection, but no matching control in the ASP.NET page. Doing this will add an unnecessary layer of complexity here. For this reason, I ll assume that the name of the dynamically created control is passed to the server using an ad hoc hidden field. (The hidden field is named HiddenField1 in this example.) Another assumption I m making here is that the dynamic control is a TextBox. Also, this kind of information can be ferried to the server using the same hidden field in any format you like:

 

protected void Page_Load(object sender, EventArgs e)

{

  if (!String.IsNullOrEmpty(HiddenField1.Value))

  {

     // Assume value contains the ID of a TextBox

     string newControl = HiddenField1.Value;

     TextBox txt = new TextBox();

     txt.ID = newControl;

     this.Form.Controls.Add(txt);

  }

}

 

On the server, if the contents of the hidden field is not null or empty, you create a new TextBox control, name it after the string in the hidden field, and, finally, add the control to the Controls collection of the page s form. This additional step ensures that a matching control will be found for the dynTextBox posted field. As a result, the dynamic control should be persisted across postbacks. If you run the page, click to create a new field as in Figure 1, and then post back, all that you get is the exception shown in Figure 2. The exception is because of an additional security barrier added in ASP.NET 2.0 to prevent script injection attacks.

 


Figure 2: The Event Validation exception.

 

In brief, by default, each ASP.NET 2.0 page contains an extra hidden field named __EVENTVALIDATION that contains a snapshot of which controls in the page should be validated during postbacks. More exactly, the hidden field lists which controls in the page can cause a postback and which values can be posted. In the page displayed to the user, the event validation field contains no information about dynTextBox. When the textbox is added and the page posts back, nothing happens as long as the no-matching control exists and the posted data is going to be ignored. But when you add a proper control that can handle the posted data, the control ID is checked against the contents of the event validation field. Because dynTextBox is not there, the exception throws.

 

Before we get to learn a bit more about event validation, there s another aspect of textbox dynamic editing that is worthwhile to point out. Imagine you have an ASP.NET TextBox control in the page and write some JavaScript code to let client users edit some of the control s properties other than the buffered text. Suppose, for example, that you change the maximum length, the read-only state, or perhaps the background color of the textbox on the client via JavaScript. None of these property changes will ever be detected on the server unless you explicitly manage to have these values ferried to the server through a hidden field:

 

var txt = document.getElementById("TextBox1");

txt.value = "New value";

txt.style.backgroundColor = "yellow";

 

The background color is lost across postbacks unless you store it in a hidden field, retrieve the value of the hidden field in the Page_Load event, and restore the property:

 

string color = HiddenField2.Value;

TextBox1.BackColor = ColorTranslator.FromHtml(color);

 

Inline Script vs. Custom Controls

Note that most of this code to manage client-side changes to HTML elements that are part of ASP.NET controls are better handled in custom controls. In other words, if you want to be able to create textboxes dynamically, edit all properties on the client, and maintain these changes across postbacks, you are better off creating your own version of the TextBox control that emits proper script code and exposes new properties to let page authors control which features should be enabled, and how. When it comes to moving to the server any custom data collected on the client, hidden fields are the only way to go. You emit hidden fields on a per-control basis (i.e., each client-creatable TextBox will have its own hidden field) and bind the two together using an arbitrary but strong naming convention. Emitting a hidden field means that you call the ClientScript s RegisterHiddenField method before rendering your custom control.

 

Next, using JavaScript, you record changes to the hidden field and process the hidden field contents on the server by implementing the IPostBackDataHandler interface on the new control. The format in which you write to the hidden field is completely up to you. At the cost of writing a bit more management code, you could also consider using just one hidden field for all controls of a certain type.

 

Event Validation in ASP.NET 2.0

The ultimate goal of event validation in ASP.NET 2.0 is ensuring that postback actions only originate from events supported and generated by HTML elements that can be unequivocally associated with a well-known list of server controls. When each page is about to render, a list of server controls that support validation is created. In this list, you find the ID of the control, as well as possible values it can post back. For example, both TextBox and DropDownList support event validation, and both have their own ID stored in the validation field. However, the DropDownList control also stores the list of displayed values any of which can be selected and posted back. The same doesn t happen with textboxes because users can type any text into a textbox by design.

 

Information about event validation is serialized to a hidden field and incorporated in the client page, just like the classic view state.

 

When a postback occurs, the ASP.NET page uses the data in the hidden field to verify that the sender of the event is a registered control and that any post is directed at registered controls and is, in the case of a list control, registered data. If any of these checks fail, you get an exception (as shown in Figure 2). For example, if you add an option to a dropdown list dynamically using JavaScript, no exception is raised as long as the selection is any of the originally displayed values. As the user selects the dynamically added option and posts backs, the exception is thrown. Event validation is enabled by default and also raises an exception if the postback doesn t include a valid event validation field.

 

How can you work around the exception shown in Figure 2 if you legitimately need to create and inject controls on the client using JavaScript? The simplest, but also less secure, approach is disabling event validation. Event validation helps prevent spoofed requests, but if you feel relatively confident, disabling the feature might not be an option. You do that by setting the EnableEventValidation attribute of the @Page directive to false:

 

<%@Page EnableEventValidation="false" %>

Another approach entails using a custom set of ASP.NET controls designed to be editable and creatable on the client and capable of interacting with the event validation subsystem in a custom way. But we ll save that for another article.

 

The sample code accompanying this article is available for download.

 

Dino Esposito is a Solid Quality Learning mentor and the author of Programming Microsoft ASP.NET 2.0 Core Reference and Programming Microsoft ASP.NET 2.0 Applications-Advanced Topics, both from Microsoft Press. Based in Italy, Dino is a frequent speaker at industry events worldwide. Join the blog at http://weblogs.asp.net/despos.

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish