We'll discuss the following.
- _VIEWSTATE
- _EVENTVALIDATION
- _EVENTTARGET
- _EVENTARGUMENT &
- The PostBack mechanism
HTTP is a stateless protocol. To make ASP.Net applications stateful, .NET uses sessions, cookies, and several hidden fields.
_VIEWSTATE
Let's create a demo app to understand ViewState.
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="PostBackDemo.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
</form>
</body>
</html>
Page Source
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<form method="post" action="Default.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE2MTY2ODcyMjlkZH0OltWWPaXud4euu7mw8G7bgQ/b9JRsQeJQCRIcL9GA" />
</div>
</form>
</body>
</html>
For an empty form with runat= “server” (an aspx page with a <form runat= “server”> element only) a hidden field “_VIEWSTATE” is also generated. In other words, if any of your elements is to be used server-side then the ASP.NET run time will create a “_VIEWSTATE” hidden field.
The _VIEWSTATE holds the state of the page when it was last processed on the server to map it unambiguously with a page request. Whenever a PostBack occurs the page class takes data stored in the view state, processes the data, regenerates the page with new values, and sends it back to the client.
Since ViewState is stored in a hidden field on the client side, it can be tampered with. To avoid this tampering with ViewState, by default View state is encoded using the Base64 schema and hashed. The hashed content is also stored in _VIEWSTATE. The hash value is calculated with the _VIEWSTATE content and server key. Now, when the page posts back, the content and hash value in _VIEWSTATE is separated. The hash value is recalculated using the _VIEWSTATE content and server key. If both hash keys match, it goes for further processing else throws a Security exception like “The state information is invalid for this page and might be corrupted”.
The ViewState is included in the form element and goes with each HTTP request and response. So, it inevitably increases the request and response size.
_EVENTVALIDATION
_EVENTVALIDATION is for validating controls. To ensure the requested data is from a known element, not a maliciously added element.
Now, let's add some client-side controls (HTML elements without runat= “server”) to our demo aspx page.
Default. aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="PostBackDemo.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<input type="text" />
<input type="button" onclick="TestFunc();" />
</div>
<script type="text/javascript">
function TestFunc() {
alert("Hey! you clicked");
}
</script>
</form>
</body>
</html>
Page Source
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<form method="post" action="Default.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTE2MTY2ODcyMjlkZH0OltWWPaXud4euu7mw8G7bgQ/b9JRsQeJQCRIcL9GA" />
</div>
<div>
<input type="text" />
<input type="button" onclick="TestFunc();" />
</div>
<script type="text/javascript">
function TestFunc() {
alert("Hey! you clicked");
}
</script>
</form>
</body>
</html>
I added a text box and a button, but there is not much change in the page source except the changes we did in the aspx page.
Now, let's make this button a server-side button (in other words Add runat= “server”).
Default. aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="PostBackDemo.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<input type="text" />
<input type="button" onclick="TestFunc();" runat="server" />
</div>
<script type="text/javascript">
function TestFunc() {
alert("Hey! you clicked");
}
</script>
</form>
</body>
</html>
Page Source
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<form method="post" action="Default.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNDIyMjM2NTY0ZGSXEZ4qwAszJ6dR3TSLdPvX3w7362I3AOBVJK+fKRnbkg==" />
</div>
<div class="aspNetHidden">
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAAK5I+PyblDBmWfKCdwXh3rh4q8s12WcMm4dben72M8A363x1W3ZmPm8tm9nmU+G2hAvlotBSLddt1LAtzvX8Omx" />
</div>
<div>
<input type="text" />
<input name="ctl02" type="button" onclick="TestFunc();" />
</div>
<script type="text/javascript">
function TestFunc() {
alert("Hey! you clicked");
}
</script>
</form>
</body>
</html>
So, now another hidden field “_EVENTVALIDATION” was added. In other words “_EVENTVALIDATION” is being added for server-side input controls.
_EVENTVALIDATION is used to ensure the postbacks and callbacks are coming from expected User Interface elements. Content is stored in _EVENTVALIDATION just before rendering the page. This feature can be enabled or disabled at the page level using EnableEventValidation in the Page directive tag.
Basically, the Page matches the content of the request with content stored in _EVENTVALIDATION to confirm that data is coming from a UI element known to the server, not from any maliciously added element.
_doPostBack, _EVENTTARGET, _EVENTARGUMENT
Now, let's add a server-side event handler to this button and a server-side button as well.
Default. aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="PostBackDemo.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<input type="text" />
<input type="button" onclick="TestFunc();" runat="server" id="btnTest" OnServerClick="Button_Click" value="Html Button" />
<asp:Button runat="server" ID="AspBtnTest" OnClick="AspButton_Click" OnClientClick="TestFunc();" Text="Asp Button" />
</div>
<script type="text/javascript">
function TestFunc() {
alert("Hey! you clicked");
}
</script>
</form>
</body>
</html>
Default.aspx.cs
using System;
namespace PostBackDemo
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterClientScriptBlock(GetType(),
Guid.NewGuid().ToString("N"), "alert('Did a postback')", true);
}
protected void AspButton_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterClientScriptBlock(GetType(),
Guid.NewGuid().ToString("N"), "alert('Asp button did a postback')", true);
}
}
}
Page Source
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<form method="post" action="Default.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNDIyMjM2NTY0ZGSXEZ4qwAszJ6dR3TSLdPvX3w7362I3AOBVJK+fKRnbkg==" />
</div>
<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
//]]>
</script>
<div class="aspNetHidden">
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAAK5I+PyblDBmWfKCdwXh3rh4q8s12WcMm4dben72M8A363x1W3ZmPm8tm9nmU+G2hAvlotBSLddt1LAtzvX8Omx" />
</div>
<div>
<input type="text" />
<input onclick="TestFunc(); __doPostBack('ctl02','')" name="btnTest" type="button" id="btnTest" value="Html Button" />
<input type="submit" name="AspBtnTest" value="Asp Button" onclick="TestFunc();" id="AspBtnTest" />
</div>
<script type="text/javascript">
function TestFunc() {
alert("Hey! you clicked");
}
</script>
</form>
</body>
</html>
2 new hidden fields “_EVENTTARGET and _EVENTARGUMENT” were created and some script having “_doPostBack()” was added. So when you have controls that can cause postback, these hidden fields and scripts are created.
Here, you can see the HTML button is not doing a direct post-back. It calls _doPostBack(eventTarget, eventArgument) and this method calls the server-side event handler or causes a post-back.
The hidden field “_EVENTTARGET” holds the ID of the control causing the postback and “_EVENTARGUMENT” contains additional information associated with the control.
Note. Controllers of type IpostBackEventHandler (implementing IpostBackEventHandler) cause a direct post-back. Other controls use a _doPostback() JavaScript function to call the server-side event handler.
There are only 2 Web server controls, Button and ImageButton, that cause a direct post-back. Others trigger a post-back using the _doPostBack() JavaScript function.
So, for controls using _doPostBack(), you can get the control that causes a postBack as follows.
protected void Page_Load(object sender, EventArgs e)
{
string ControlId = Request.Params.Get("_EVENTTARGET");
if (!string.IsNullOrEmpty(ControlId))
{
Page.ClientScript.RegisterStartupScript(GetType(),
Guid.NewGuid().ToString("N"),
"alert('Control with ID:" + ControlId + " caused postback')", true);
}
}
But, for Buttons and ImageButtons you need to call the _doPostBack() with button Id explicitly OnClientClick(). Then, follow the same procedure to get the element causing the PostBack().
From the code above remove the <input type= “button”> element and keep the <asp: Button>, you can see there is no _EVENTTARGET and _EVENTARGUMENT hidden field or _doPostBack() JavaScript function since asp: Button is doing a direct postback.
Default. aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="PostBackDemo.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<input type="text" />
<asp:Button runat="server" ID="AspBtnTest" OnClick="AspButton_Click" OnClientClick="TestFunc();" Text="Asp Button" />
</div>
<script type="text/javascript">
function TestFunc() {
alert("Hey! you clicked");
}
</script>
</form>
</body>
</html>
Default.aspx.cs
using System;
namespace PostBackDemo
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void AspButton_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterClientScriptBlock(GetType(),
Guid.NewGuid().ToString("N"), "alert('Asp button did a postback')", true);
}
}
}
Page Source
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="Script/jquery-1.11.1.js"></script>
</head>
<body>
<form method="post" action="Default.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA0OTM4MTAwNGRkB/iWAnhisFL/BNe0slZ319eTcGFZayj3aW2rKeptEAk=" />
</div>
<div class="aspNetHidden">
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEdAAKrZZDeDrjNeyuPPqXKcefNI5oLptWPTPFd2fcP0LUic9/ED7QKOeuxn48nwM/QT7XNadP1I+7bSAmOnoTtiPYK" />
</div>
<div>
<input type="text" />
<input type="submit" name="AspBtnTest" value="Asp Button" onclick="PostBack();" id="AspBtnTest" />
</div>
<script type="text/javascript">
function TestFunc() {
alert("Hey! you clicked");
}
</script>
</form>
</body>
</html>