ViewState and JavaBeans
Summary: Learn how ViewState in ASP.NET makes validation easier than using JavaBeans in JSP, by automatically persisting form data and returning it to the user if validation fails.
Introduction
Submission of a Web form is usually a two-stage process. The first stage is to validate the content of the form fields to ensure that it is valid and falls within the allowed limitations of the data structure. The second stage, submitting the form to an underlying application for processing, occurs only after validation has been successful. In this way, developers can be sure that the processing application will be called only once, and that it will always receive data it knows how to handle.
In most cases, validation is accomplished by having the form submit back to itself for validation. That is, if the form is on a page called Register.jsp, clicking the Submit button will send the form data to Register.jsp. Register.jsp will contain not only the HTML for the form itself, but also JavaScript code to examine each submitted field in the form and determine whether or not it is valid.
Similarly, in Microsoft® ASP.NET, all forms are posted back to the current .aspx page for validation. This process is called POSTBACK. When the page is requested for the first time, there is no additional information sent in the POST request and therefore the form appears blank. When the form is filled out and the Submit button is clicked, the same .aspx page is requested for a second time. This time, however, there are additional parameters included in the POST request (the values of the fields); the server recognizes this and performs validation on those parameters, forwarding to the appropriate page if they are all, indeed, valid.
But what happens if the form isn't valid? In both JSP and ASP.NET, we will want to redisplay the page for the user so that they can correct errors in invalid fields. However, we don't (usually) want the user to have to re-enter all the form data from scratch. So how do we choose to maintain data in some or all of the form fields, even after the page is reloaded?
In this article, we will discuss various ways of persisting form data after submission. We will examine the most commonly used techniques in both JSP and ASP.NET, and then look at how ASP.NET can be used to simplify the entire process, abstracting it almost completely into the background.
Means of Persisting Form Data
There are many ways of persisting form data after the form has been submitted. Some of the more popular ways include the following:
- The values of form fields can be stored in the Session object. This is what we do in the CodeNotes Web site; each user of the site has a unique session ID that identifies him or her and allows user data to persist throughout a visit to the site. Data can be added to the Session object with a line like this (in ASP.NET):
· Session("Name") = "Bob Jones";
Session information can be stored in various locations: inside the ASP.NET runtime process, inside a dedicated Microsoft Windows® service, or inside a Microsoft SQL Server™ database. However, using the Session object, in any of these locations, is costly in server memory. In addition, you have to read the values out of session and put them back into the form on each page load. This routine code bulks up your pages.
- Cookies are another way of persisting data during (and between) user visits to a Web application. Unlike the Session object, cookies are stored on the individual user's machine, and are requested by the application itself each time the user visits. Cookies, however, take additional development time, and also require that all users have cookies enabled in their browsers—something that many people choose not to do for security reasons.
- Another option to persisting data is to duplicate your form content in a hidden field that is posted back to the server. The server can then parse the hidden field and rewrite the page HTML by inserting the previously entered values. Hidden fields, like cookies and session storage, require additional "plumbing code" and can be difficult to maintain if the form changes even slightly.
- One of the most popular methods of persisting form data in JSP is by using accessory data objects, such as JavaBeans. In the next section, we will discuss what JavaBeans are, how JavaBeans are used in a simple JSP application to persist form data, and look at an example of such an application.
JavaBeans and JSP
Although we store information in the Session object on codenotes.com, a more "proper" JSP alternative is to use JavaBeans. This involves designing a JavaBean class to represent the data structure of a form, and then accessing the bean when needed from a JSP page using a special syntax.
What are JavaBeans?
A JavaBean is a Java class that has member variables (properties) exposed via get and set methods. JavaBeans can be used for almost any purpose, from visual components to data elements. With regards to JSP, JavaBeans are generally data objects, and they follow some common conventions:
- The Java class is named SomethingBean and should optionally implement the Serializable marker interface. This interface is important for beans that are attached to a Session object that must maintain state in a clustered environment, or if the JavaBean will be passed to an Enterprise JavaBean (EJB).
- Each bean must have a constructor that has no arguments. Generally, the member variables are initialized in this constructor.
- Bean properties consist of a member variable, plus at least one get method and/or a set method for the variable. Boolean values may use an is method instead of get (for example, isConfirmed()).
- The member variables commonly have a lowercase first letter in the first word, with subsequent words capitalized (for example, firstName). The get and set methods are named getPropertyName and setPropertyName, where the property name matches the variable name (for example, getFirstName).
- Get and set accessor methods often perform operations as simple as returning or setting the member variable, but can also perform complex validation, extract data from the database, or carry out any other task that acts on the member variables.
JavaBean Syntax
A simple JavaBean class might look something like Listing 1.
Listing 1. Simple JavaBean class (UserBean)
package com.codenotes.UserBean;
public class UserBean() {
private String firstName;
private String lastName;
//default constructor
public UserBean{
this.firstName = "";
this.lastName = "";
}
//get methods
public String getFirstName() {return firstName;}
public String getLastName() {return lastName;}
//set methods
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
This class has get and set methods for two fields: firstName and lastName. Notice that this class exactly follows the conventions listed previously.
To use the Bean from a JSP script, we need only add the code in Listing 2 to the top of the JSP.
Listing 2. Using UserBean
id="UserBean"
class="com.codenotes.UserBean"
scope="session"/>
property="*"/>
The element creates an instance of the UserBean class and assigns it session scope, which means it will remain available until the end of a user's entire session with your Web application. The element, in this case, populates the data structure of the JavaBean with the information in the Request object. Note that this will only work if the fields in the request exactly match fields in the JavaBean.
We can now access the data stored in the bean from anywhere in the JSP, no matter how many times it is reloaded, by using code like Listing 3.
Listing 3. Getting values from a JavaBean
The JSP processor automatically interprets getProperty and setProperty methods and calls the appropriate methods in the Bean class itself.
Server-side Validation Using JavaBeans
In an ordinary HTML page, the only way to validate user input is on the client side using JavaScript. However, client side validation can be problematic as it depends on the client's browser properly implementing your JavaScript code. In addition, a malicious user can easily download your page, make modifications to disable the JavaScript and work around your validation.
Using JavaBean tags, however, you can easily make a "validation bean" that will perform secure server-side validation on your data entry fields. Once you are sure that the data is valid, you can transfer it from this bean to any back-end system, such as a database or EJB. The validation bean thus becomes an intermediate step which helps secure your Web forms without requiring a significant modification to your middle tier or back end data systems.
ValidationBean
ValidationBean might look something like Listing 4.
Listing 4. A ValidationBean
package com.codenotes;
import java.util.Vector;
public class ValidationBean {
private String m_email = "";
private String m_name = "";
private int m_age = 0;
private Vector messages = new Vector();
public ValidationBean() {
m_email = "";
m_name = "";
m_age = 0;
}
public String getEmail() {return m_email;}
public void setEmail(String email) {m_email = email;}
public void isValidEmail() {
//check for @ symbol somewhere in string
if ((m_email.length() == 0) || (m_email.indexOf("@") <>
messages.add("Enter a valid email.");
}
}
public String getName() {return m_name;}
public void setName(String name) {m_name = name;}
public void isValidName() {
//check if name exists
if (m_name.length() <>
messages.add("Name is required");
}
}
public int getAge() {return m_age;}
public void setAge(int age) {m_age = age;}
public void isValidAge() {
//must be at least 18 years old
if (m_age <>
messages.add("You must be 18 years old to register.");
}
}
public String[] getMessages() {
isValidName();
isValidAge();
isValidEmail();
return (String[])messages.toArray(new String[0]);
}
}
The code in Listing 4 contains some interesting features. First, you should note that every bean you make for use in a JSP should be assigned to a package. If you don't assign the bean to a package, most servlet containers will automatically assume it's part of the automatically created package when the JSP is compiled. This problem also occurs with custom tag handlers.
Second, although the isValidXXX() functions traditionally return a Boolean value, in our case we have chosen to simply add a message to our message vector instead. The isValidXXX() functions are meant to be called internally. From the JSP, we simply call getMessages() and check the length. If any messages are present, then some data is invalid.
Finally, if we wanted a more advanced sort of validation, we can easily expand the logic in each of the isValidXXXX() methods. For example, we could set the email field to be valid if it is missing, or it has the proper format (in other words, the field would be optional).
Using ValidationBean
The JSP code itself will be very similar to that discussed in the previous section. Listing 5 shows an example of a form using ValidationBean.
Listing 5. Form using ValidationBean
class="com.codenotes.ValidationBean">
property="*"/>
Please change the following fields:
page="CompleteForm.jsp" />
method="post">
value='
property="name"/>' />
Name
value='
property="age" />' />
Age
value='
property="email" />' />
Email
/>
The JSP page in Listing 5 performs the following actions:
- Populates an instance of ValidationBean with data from the Request object.
- Queries the bean to see if it has any messages in its messages vector.
- If there are error messages in the vector, the page displays a list of the error messages and redisplays the form using the data from the Bean to fill out the fields, where available.
- If there are no error messages in the vector, the Bean is added to the user's current session, and the user is then forwarded to the next appropriate page.
Note that we have to differentiate between single quotes and double quotes within each tag. If we don't switch quote types, the servlet container may become confused and parse the tags incorrectly.
Using server-side validation offers many advantages over JavaScript and client-side code. Although you do have to write more boilerplate code in developing your JavaBeans, the resulting savings and simplicity in your JSP more than make up for it.
JavaBeans Example
As mentioned previously, the CodeNotes Web site uses the Session object instead of JavaBeans to persist form data, so there is no example of JavaBeans we can extract from the CodeNotes site. Instead, for the example in this article, we will design a simple Registration form similar to the one located at http://www.codenotes.com/login/registerAction.aspx, except that it uses JavaBeans instead of Session. In the next two major sections of this article, we will convert the example to ASP.NET using the Java Language Conversion Assistant (JLCA) to see how conversion of JavaBeans works, and then we will design a brand new Registration form in ASP.NET and show how new features in ASP.NET make persisting form data trivial.
Note that to keep this example simple, we will not do any sort of validation on form data.
UserBean
UserBean is a straightforward JavaBean with basic getters and setters for the data in the form. The only thing to note is that we've put it in package codenotes. JavaBeans should always be placed in packages; otherwise, the servlet processor will assume they are in a default package and won't be able to find them there. Listing 6 shows the code for UserBean.java.
Listing 6. UserBean.java
package codenotes;
public class UserBean implements java.io.Serializable {
private String userName;
private String password;
private String firstName;
private String lastName;
private String displayName;
public UserBean() {
this.userName="";
this.password="";
this.firstName="";
this.lastName="";
this.displayName="";
}
public String getUserName() {return userName;}
public String getPassword() {return password;}
public String getFirstName() {return firstName;}
public String getLastName() {return lastName;}
public String getDisplayName() {return displayName;}
public void setUserName(String userName) {
this.userName=userName;
}
public void setPassword(String password) {
this.password=password;
}
public void setFirstName(String firstName) {
this.firstName=firstName;
}
public void setLastName(String lastName) {
this.lastName=lastName;
}
public void setDisplayName(String displayName) {
this.displayName=displayName;
}
}
Register.htm
Register.htm is a simple HTML file that will contain the form for the user to fill out. It contains no special tags or JavaScript code of any kind; it simply uses Register.jsp (described in the next section) as its target ACTION. We're also going to use HTTP GET instead of POST, so you can see the parameters in the URL. Listing 7 shows Register.htm.
Listing 7. Register.htm
Registration Form
action="welcome.jsp" method="get" id="regForm"
name="regForm">
UserName/Email: | |
Password: | |
Re-enter Password: | |
FirstName: | |
LastName: | |
DisplayName: | |
|
Welcome.jsp
Finally, on Welcome.jsp we simply display the data that was entered into the form on the page. This is where we populate a UserBean instance with the results of the form submission, and then use tags to access the information from the Bean, when needed. Listing 8 shows Welcome.jsp.
Listing 8. Register.jsp
id="UserBean" class="codenotes.UserBean"
scope="session" />
name="UserBean" property="*" />
Welcome!
Your registration data:
User Name: | property="userName" /> |
Password: | property="password" /> |
First Name: | property="firstName" /> |
Last Name: | property="lastName" /> |
Display Name: | property="displayName" /> |
Converting JSP to ASP.NET by Using JLCA
The Java Language Conversion Assistant (JLCA) converts a JavaBean class into a Microsoft® .NET class that implements System.Runtime.Serialization.ISerializable. For each pair of get and set methods in the original Bean class, JLCA creates a property (virtual public object) with get and set accessors. An ASP.NET page can then create an instance of the serializable class and access its properties through that instance.
We'll convert the UserBean example from the previous section to an ASP.NET application. If you run the conversion wizard on the application found in the jlcademo.msi, you should get an almost "perfect" conversion, with no warnings or errors at all.
JLCA will leave register.htm as is, and will convert UserBean.java to a C# file named UserBean.cs. Examining UserBean.cs, we can see how each of UserBean's get/set pairs has been converted to a property, like the one shown in Listing 9.
Listing 9. A UserBean property
virtual public System.String UserName
{
get
{
return userName;
}
set
{
this.userName = value;
}
}
In order for this class to compile correctly, however, you will need to implement a method called getObjectData(), which is required of any class that implements the ISerializable interface. You don't need to write any code for this. Simply do the following:
- Switch to the Class View instead of the Solution Explorer.
- Expand beansConvcodenotesUserBeanBases and Interfaces.
- Right-click ISerializable, and then click AddImplement Interface.
This will add the necessary implementation code for getObjectData() to your class, and conceal it within a region so you don't have to worry about it.
The welcome.jsp file from the previous example converts perfectly into welcome.aspx, so you don't need to make any changes there. In fact, all you need to do now to get the application running is to right-click register.htm in the Solution Explorer, and then click Set as Start Page. After that, you can run the application and see that it is functionally identical to the JavaBean example from the previous section.
One thing you may notice is that JLCA has added a significant amount of additional code to the beginning of welcome.aspx. This code does two things:
- Checks to see if the user's current session already contains an instance of UserBean. If it doesn't, it creates a new, empty UserBean.
- Populates the UserBean with values from the Request object, if there are any. Because UserBean.cs implements ISerializable, it is able to populate a collection representing the properties of the Bean and then cycle through them, adding the correct value from the Request object to the correct property.
This code replaces the JSP container code that performed the same actions when a tag was encountered. As you can see, the JLCA does its best to ensure that your converted application remains as faithful as possible to the functionality of the original JSP code.
The ASP.NET Alternative
Instead of using an accessory data object like a JavaBean to store field data during validation, ASP.NET adds a special hidden field named __VIEWSTATE to the generated source for every form. This hidden field stores the state of all controls on the page, such as the text entered in a text box, whether checkboxes are checked or unchecked, the contents and selected items in list boxes, and so on. Therefore, there is no need for you to add additional code to persist field values each time a Submit button is clicked and validation is performed.
Traditionally, many ASP applications used standard hidden fields to store field data between validation attempts. ViewState alleviates several problems with normal hidden fields, including:
- Normally, extra code was required to put field values into hidden fields for storage upon submission. ASP.NET does not require any extra code, as it automatically serializes the values of all fields on the page into a single __VIEWSTATE hidden field.
- The names and content of hidden fields are usually easily readable in the source code for an HTML form. The __VIEWSTATE field, on the other hand, is encoded using a complex hash scheme and is unreadable to humans. Only allowed applications will be able to decrypt the __VIEWSTATE field and extract values from its contents.
- You can specifically identify which controls should or shouldn't be included in the __VIEWSTATE field, and even assign a page level directive to disable ViewState if you choose. All of these settings are controlled with simple properties, and no coding is required.
Although the use of the __VIEWSTATE field in ASP.NET may seem like an internal implementation detail of the Framework, it can tangibly influence the performance of your applications. Every time the server must update a page, the contents of the form on the page are actually sent to the client twice; first, as regular HTML, and then as encoded values in the __VIEWSTATE field. If an application has many controls that contain a lot of information, this hidden field can grow to sizable proportions. Because this field is transmitted as part of the response to a client in ASP.NET, it can adversely affect transmission time.
Using ViewState
By default, most controls in ASP.NET automatically have ViewState enabled. This means that you don't have to do anything special in order to have them persist data between validation attempts.
Let's look at an example. We'll create a simple ASP.NET application that uses several common controls. As usual, we can simply drag components onto the Design View of the Web form. There's no need to add any additional code. In this case, we're going to add a text box, a calendar, and a button.
If you select any one of the controls and examine its enableViewState property in the properties box, you will see that, by default, it is set to true. This means that the state of the control will be stored in the hidden __VIEWSTATE field, and will be persisted even after clicking the Go! button.
Run the application. Type your name in the field and select a date on the calendar. Then click Go! Since we didn't add any code to the button, the form will simply post and refresh on the same page, but as you will see, the text you entered in the field and the date you selected on the calendar remain exactly as they were. You can examine the content of the __VIEWSTATE field by right-clicking on the page, and then clicking View Source. Somewhere near the top of the page, you'll see an element that looks like the one in Listing 10.
Listing 10. A __VIEWSTATE example
value="dDw3MDY2NzMxNDI7dDw7bDxp...7Pj47Phdg9e+N3tG/uHE9I7KBRj6NR9Oe"/>
This element contains information on the state of each control in the page. You can watch it change, for example, by modifying the date you selected in the calendar.
Disabling ViewState
If, for some reason, you don't want an element to persist its state between posts, it is very easy to disable the ViewState on that element. Simply change the value of the enableViewState property for a particular element to "false" instead of "true." Try disabling ViewState on both the textbox and the calendar and then running the example application again. Type some text in the field and select a date as before, and then click Go!
What happens? The calendar resets itself to its original state, and the date you selected is lost. Because you have configured your calendar not to store its content in __VIEWSTATE, the application will have no memory of any manipulation of that control after a postback to the server.
However, you may have noticed that the text in your text box remained, even though you disabled ViewState for it as well. This is because certain simple control properties (like the text content of a text box) can be stored as basic text, and therefore ViewState is not necessary (and thus not implemented) to persist the values. ASP.NET simply obtains the value from the Request object instead. However, if you were to dynamically set a more complex property of the text box, such as its background color (for example, with a Change Color button), that change would be lost if ViewState were disabled on the text box.
Note that you can also disable ViewState for an entire ASP.NET page by clicking on the form and changing the value of its enableViewState property to "false."
enableViewStateMac
If you looked at the enableViewState property of an ASP.NET form, you may also have noticed a property below it named enableViewStateMac. MAC stands for Machine Authentication Code. When this property is set to true, ASP.NET will include a machine authentication code in the __VIEWSTATE field. This prevents tampering, as it ensures that only the machine that encoded the __VIEWSTATE field in the first place can decode it and determine field values from it. There is generally no reason to turn this feature off.
Advantages of Using ViewState
The primary advantages of the ViewState feature in ASP.NET are:
- Simplicity. There is no need to write or generate complex JavaBean classes in order to store form data between submissions. ASP.NET does everything for you automatically, and you can simply turn ViewState off if you don't want to use it for a particular control. Basically, persistence is all done in the background.
- Flexibility. It's easy to configure ViewState on a control-by-control basis, so you can have certain fields maintain themselves so that the user does not have to re-enter them, and have other fields reset every time to ensure that the user enters them correctly. There is no need to ensure that the data submitted by your form fits a particular data structure, as the __VIEWSTATE field is encoded and decoded on the fly, with all the information in the correct location and order.
Limitations of Using ViewState
The primary limitations of ViewState are:
- You can't transfer ViewState information from page to page. With JavaBeans, you can simply store the JavaBean in the session and then access it again from somewhere else. This is not possible with ViewState, so if you want to store information in a user's session for later you need to create your own data object for storage (or store each field individually).
- ViewState is not suitable for transferring data for back-end systems. That is, you still have to transfer your form data over to the back end using some form of data object. With a JavaBean, you simply transfer a reference to the JavaBean and let the back end extract whatever data it needs.
This article discusses solutions to a problem that will arise in almost every Web application: How to store form data during validation (in other words, how to persist form data between calls to the server).
In JSP, JavaBeans are the most common solution. JavaBeans are special classes that follow a particular structure: for each field you want to store, you have a get and set method for retrieving and modifying a value in the bean. You can then invoke and access JavaBeans from within JSP code using special tags.
In ASP.NET, the state of fields is stored in an encoded, hidden field called __VIEWSTATE. Addition of form data to __VIEWSTATE is done automatically by ASP.NET, and there is no need to manually create any auxiliary data objects. ViewState is extremely easy to use and always appropriate for simple persistence of data during validation. For more complicated data transfers, however, such as persisting data across different pages, you will need to create and use a data object.