ASP.NET pages supply the ViewState
property to let applications build a call context and retain values
across two successive requests for the same page. The view state
represents the state of the page when it was last processed on the
server. The state is persisted—usually, but not necessarily, on the
client side—and is restored before the page request is processed.
By default, the view state is maintained as a
hidden field added to the page. As such, it travels back and forth with
the page itself. Although it is sent to the client, the view state does
not represent, nor does it contain, any information specifically aimed
at the client. The information stored in the view state is pertinent
only to the page and some of its child controls and is not consumed in
any way by the browser.
Using the view state has advantages and
disadvantages that you might want to carefully balance prior to making
your state management decision. First, the view state does not require
any server resources and is simple to implement and use. Because it’s a
physical part of the page, it’s fast to retrieve and use. This last
point, that in some respects is a strong one, turns into a considerable
weakness as soon as you consider the page performance from a wider
perspective.
Because the view state is packed with the page,
it inevitably charges the HTML code transferred over HTTP with a few
extra kilobytes of data—useless data, moreover, from the browser’s
perspective. A complex real-world page, especially if it does not even
attempt to optimize and restrict the use of the view state, can easily
find 20 KB of extra stuff packed in the HTML code sent out to the
browser.
In summary, the view state is one of the most
important features of ASP.NET, not so much because of its technical
relevance but because it allows you to benefit from most of the magic of
the Web Forms model. Used without strict criteria, though, the view
state can easily become a burden for pages.
1. The StateBag Class
The StateBag
class is the class behind the view state that manages the information
that ASP.NET pages and controls want to persist across successive posts
of the same page instance. The class works like a dictionary and, in
addition, implements the IStateManager interface. The Page and Control base classes expose the view state through the ViewState property. So you can add or remove items from the StateBag class as you would with any dictionary object, as the following code demonstrates:
ViewState["FontSize"] = value;
You should start writing to the view state only after the Init
event fires for the page request. You can read from the view state
during any stage of the page life cycle, but not after the page enters
rendering mode—that is, after the PreRender event fires.
View State Properties
Table 1 lists all the properties defined in the StateBag class.
Table 1. Properties of the StateBag Class
Property | Description |
---|
Count | Gets the number of elements stored in the object. |
Item | Indexer property. It gets or sets the value of an item stored in the class. |
Keys | Gets a collection object containing the keys defined in the object. |
Values | Gets a collection object containing all the values stored in the object. |
Each item in the StateBag class is represented by a StateItem object. An instance of the StateItem object is implicitly created when you set the Item indexer property with a value or when you call the Add method. Items added to the StateBag object are tracked until the view state is serialized prior to the page rendering. Items serialized are those with the IsDirty property set to true.
View State Methods
Table 2 lists all the methods you can call in the StateBag class.
Table 2. Methods of the StateBag Class
Method | Description |
---|
Add | Adds a new StateItem object to the collection. If the item already exists, it gets updated. |
Clear | Removes all items from the current view state. |
GetEnumerator | Returns an object that scrolls over all the elements in the StateBag. |
IsItemDirty | Indicates whether the element with the specified key has been modified during the request processing. |
Remove | Removes the specified object from the StateBag object. |
The IsItemDirty method represents an indirect way to call into the IsDirty property of the specified StateItem object.
Note
The view state for the page is a cumulative property that results from the contents of the ViewState property of the page plus the view state of all the controls hosted in the page. |
2. Common Issues with View State
Architecturally
speaking, the importance of the view state cannot be denied, as it is
key to setting up the automatic state management feature of ASP.NET. A
couple of hot issues are connected to the usage of the view state,
however. Most frequently asked questions about the view state are
related to security and performance. Can we say that the view state is
inherently secure and cannot be tampered with? How will the extra
information contained in the view state affect the download time of the
page? Let’s find out.
Encrypting and Securing
Many developers are doubtful about using the
view state specifically because it is stored in a hidden field and left
on the client at the mercy of potential intruders. Although the data is
stored in a hashed format, there’s no absolute guarantee that it cannot
be tampered with. The first comment I’d like to make in response to this
is that the view state as implemented in ASP.NET is inherently more
secure than any other hidden fields you might use (and that you were
likely using, say, in old classic ASP applications). My second remark is
that only data confidentiality is at risk. While this is a problem, it
is minor compared to code injection.
Freely accessible in a hidden field named
__VIEWSTATE, the view state information is, by default, hashed and
Base64 encoded. To decode it on the client, a potential attacker must
accomplish a number of steps, but the action is definitely possible.
Once decoded, though, the view state reveals only its contents—that is,
confidentiality is at risk. However, there’s no way an attacker can
modify the view state to post malicious data. A tampered view state, in
fact, is normally detected on the server and an exception is thrown.
For performance reasons, the view state is not encrypted. If needed, though, you can turn the option on by acting on the web.config file, as follows:
<machineKey validation="3DES" />
When the validation attribute is set to 3DES,
the view-state validation technique uses 3DES encryption and doesn’t
hash the contents.
Machine Authentication Check
The @Page directive contains an attribute named EnableViewStateMac,
whose only purpose is making the view state a bit more secure by
detecting any possible attempt at corrupting the original data. When
serialized, and if EnableViewStateMac is set to true, the view state is appended with a validator hash string based on the algorithm and the key defined in the <machineKey> section of the configuration file. The resulting array of bytes, which is the output of the StateBag’s
binary serialization plus the hash value, is Base64 encoded. By
default, the encryption algorithm to calculate the hash is SHA1, and the
encryption and decryption keys are auto-generated and stored in the Web
server machine’s Local Security Authority (LSA) subsystem. The LSA is a
protected component of Windows NT, Windows 2000, Windows Server 2003,
and Windows XP. It provides security services and maintains information
about all aspects of local security on a system.
If EnableViewStateMac is true,
when the page posts back, the hash value is extracted and used to
verify that the returned view state has not been tampered with on the
client. If it has been, an exception is thrown. The net effect is that
you might be able to read the contents of the view state, but to replace
it you need the encryption key, which is in the Web server’s LSA. The MAC in the name of the EnableViewStateMac property stands for Machine Authentication Check,
which is enabled by default. If you disable the attribute, an attacker
could alter the view-state information on the client and send a modified
version to the server and have ASP.NET blissfully use that
tampered-with information.
To reinforce the security of the view state, in ASP.NET 1.1 the ViewStateUserKey property has been added to the Page
class. The property evaluates to a user-specific string (typically, the
session ID) that is known on the server and hard to guess on the
client. ASP.NET uses the content of the property as an input argument to
the hash algorithm that generates the MAC code.
Size Thresholds and Page Throughput
My personal opinion is that you should be
concerned about the view state, but not for the potential security holes
it might open in your code—it can let hackers exploit only existing
holes. You should be more concerned about the overall performance and
responsiveness of the page. Especially for feature-rich pages that use
plenty of controls, the view state can reach a considerable size,
measured in KB of data. Such an extra burden taxes all requests, in
downloads and uploads, and ends up creating serious overhead for the
application as a whole.
What would be a reasonable size for an ASP.NET
page? And for the view state of a page? Let’s take a look at a sample
page that contains a grid control bound to about 100 records (the
Customers table in the Northwind database of SQL Server):
<html>
<head runat="server">
<title>Measure Up Your ViewState</title>
</head>
<script language="javascript">
function ShowViewStateSize()
{
var buf = document.forms[0]["__VIEWSTATE"].value;
alert("View state is " + buf.length + " bytes");
}
</script>
<body>
<form id="form1" runat="server">
<input type="button" value="Show View State Size"
onclick="ShowViewStateSize()">
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
SelectCommand="SELECT companyname, contactname, contacttitle
FROM customers"
ConnectionString="<%$ ConnectionStrings:LocalNWind %>"
<asp:DataGrid ID="grid" runat="server"
DataSourceID="SqlDataSource1" />
</form>
</body>
</html>
In ASP.NET 2.0 and beyond, the total size of
the page is about 20 KB. The view state alone, though, takes up about 11
KB. If you port the same page back to ASP.NET 1.x, results are even
worse. The whole page amounts to 28 KB, while the view state alone
amounts to a burdensome 19 KB. Two conclusions can be drawn from these
numbers:
Starting with ASP.NET 2.0, the
view-state field appears to be more compact.
The view
state takes up a large share of the downloaded bytes for the page. You
won’t be too far from the truth if you estimate the view-state size to
be about 60 percent of the entire page size.
What can you do about this? First, let’s play
with some numbers to determine a reasonable goal for view-state size in
our applications. You should endeavor to keep a page size around 30 KB,
to the extent that is possible of course. For example, the Google home
page is less than 4 KB. The home page of http://www.asp.net
amounts to about 50 KB. The Google site is not written with ASP.NET, so
nothing can be said about the view state. But what about the view-state
size of the home of http://www.asp.net? Interestingly enough, that page has only 1 KB of view state. On the other hand, the page http://www.asp.net/ControlGallery/default.aspx?Category=7&tabindex=0
is larger than 130 KB, of which 10 KB is view state. (This page has
been redesigned lately; a few months ago, I measured it to be 500 KB in
size, with 120 KB of view state!)
The ideal size for a view state is around 7 KB;
it is optimal if you can keep it down to 3 KB or so. In any case, the
view state, regardless of its absolute size, should never exceed 30
percent of the page size.
Note
Where
do these numbers come from? “From my personal experience,” would
perhaps be a valid answer, but not necessarily a good and exhaustive
one. Let’s put it this way: the smallest you can keep a page is the best
size. To me, 30 KB looks like a reasonable compromise, because most
things can be stuffed into that size. Clearly, if you have 250 items to
display, your page size can grow up to 1 MB or so. In the end, having a
smaller or larger view state is a design choice and is mostly
application-specific. Within these boundaries, though, a few guidelines
can be stated. The most important guideline is not so much that view
state should be limited to a few KB, but that it should take a minimal
percentage of the overall page size. Which percentage? Being the
view-state helper, I’d say no more than 25 percent or 30 percent at the
most. But here I’m just throwing out numbers using a bit of common
sense. If you can disable the view state altogether, do it. At the very
least, you should avoid storing there the avoidable items that don’t
change often and are easily cached on the server, such as a long list of
countries. |