Tuesday, September 25, 2007

Conditional UpdatePanel and Event Validation

Many people find it mysterious when they get a message box with the following error message:

Invalid postback or callback argument.  Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%@ Page EnableEventValidation="true" %> in a page.  For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them.  If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.

Unfortunately ASP.NET does not provide enough information to troubleshoot this exception easily.  I am going to explain reasons that can cause this exception and give a tip on how to troubleshoot it.

The event validation is designed to prevent malicious requests from breaking your software.  When a server page is rendered and sent to the browser web controls tell ASP.NET about all allowed values that the web page can submit back to the server.  The collected values are hashed and stored in the hidden input in the web page.  When the postback values are received from the client postback values are validated against the hashes stored in this hidden input.

<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWDALLu5z7BQKdkKesBQL0pYTCDAL1pYTCDAL2pYTCDAL3pYTCDALwpYTCDALxpYTCDALypYTCDALjpYTCDALspYTCDAL0pcTBDAn2Nrc2z9979msu0SdahUlFp5dq" />

If server encounters any value with a hash which is among hashes in the __EVENTVALIDATION field it throws an exception with the message above.  If you develop with pure ASP.NET and receive such exception it usually means that someone attempts to tamper a request and does it incorrectly.

With the ASP.NET AJAX extensions however it becomes possible to get this exception as a result of developer mistake.

I've constructed a sample which demonstrates what you have to do (really you should not do) to get this weird message box. 

<%@ Page Language="C#" AutoEventWireup="true" %>

<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<
html xmlns
="http://www.w3.org/1999/xhtml">
<
head runat
="server">
<title>Conditional Update Panel and Event Validation</title
>
</
head
>
<
body
>
<form id="form1" runat
="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"
/>
<div
>
<asp:UpdatePanel runat="server" ID
="FirstUpdatePanel">
<ContentTemplate
>
<asp:Button runat="server" ID="ClickMeButton" Text="Click Me!"
/>
</ContentTemplate
>
</asp:UpdatePanel
>
<asp:UpdatePanel runat="server" ID="UpdatePanel" UpdateMode
="Conditional">
<ContentTemplate
>
<asp:DropDownList runat="server" ID
="DropDownList">
</asp:DropDownList
>
</ContentTemplate
>
</asp:UpdatePanel
>
</div
>
</form
>
</
body
>
</
html
>

<
script runat
="server">

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
int startCounter = (int?)ViewState["Counter"] ?? 0;
startCounter++;
ViewState["Counter"] = startCounter;

DropDownList.Items.Clear();
for (int i = startCounter; i < startCounter + 10; i++)
{
ListItem listItem = new ListItem();
listItem.Value = i.ToString();
listItem.Text = i.ToString();
DropDownList.Items.Add(listItem);
if (i == startCounter)
{
listItem.Selected = true;
}
}
//UpdatePanel.Update(); // Uncomment this line to fix the sample!
}
</script
>


If you run this sample and click the button twice you will see exactly the same message box.  So, what is causing ASP.NET to throw an exception in the sample above?

The sample changes items in the DropDownList on each postback and changes them in a way that previously selected item disappears from the list on the next postback.  (Similar things happen if you implement cascading dropdowns with autopostbacks).  If you disable partial rendering by setting ScriptManager property

EnablePartialRendering="false"

you will see that the page works perfectly.  DropDownList tells ASP.NET about all values in the items collection while it is been rendered and ASP.NET successfully validates submitted value against hashes stored in the "event validation" hidden input on the next postback.  If you re-enable partial rendering in this sample, one small but still important thing changes.  Render method of the DropDownList is still invoked on every postback, but output of the render method is not sent to the browser, because it is in the UpdatePanel which is not updated in response to button click.  (I intentionally made button to cause the asynchronous postback without updating second UpdatePanel).  Now, __EVENTVALIDATION hidden input is successfully updated with new hash values for DropDownList, but the DropDownList itself remains unchanged on the client and still submits old selected value on the next postback.  When the server receives this value it cannot validate it against the new set of hashes in the "event validation" hidden input and throws he exception.

So, the conclusion is very simple: "Don't change controls during the async postback that are outside an update panel or are in the update panel which is not updated during this asynchronous postback."  Or vice versa: "If you have conditional update panels force them to update their client state if you change controls inside the update panel." (See commented code in the sample and try uncommenting it).

Downloadable version of the sample: EventValidation.zip

Troubleshooting


It is not always easy to find what causes the "event validation failed" problem in the complex ASP.NET page.  I recommend following steps to find the control causing the problem:


  1. Open Visual Studio and configure your website to run in debugging mode, if haven't done it already.

  2. Be sure that your Visual Studio is configured to show exceptions when they are thrown
     Enabling Exceptions

  3. Disable "Exception Assistance" and "Just My Code" debugger features in the options dialog box
    Setting Debugging Options

  4. Start your web site under the debugger and reproduce the exception (you may get some other internal ASP.NET exceptions while starting the website - click "continue" until you get the "event validation failed" exception
    Exception

  5. Click on break button and open "call stack" tool window
    CallStack

  6. The selected stack frames on the following screen-shot tells you that the problematic control is DropDownList
    SmallCallStack

  7. Unfortunately it is not always possible to find what particular control causes the exception, but for DropDownList it is typically possible to do.  Double click the DropDownList.LoadPostData stack frame and open "Locals" debug tool window.
    Locals
    You should be able to expand "this" value.  (I don't know if you can find a stack frame with accessible this variable for other controls. )

  8. The final step is to get the UniqueID of the failing control.  Just open a QuickWatch and type UniqueID there.
    QuickWatch

Now when you know which controls causes the problem you should check:


  • whether you change properties of this controls and whether you need to change them.  If so, 

  • whether this control is in the update panel and whether this update panel is updated during the asynchronous postback

More Cases Causing the Same Problem



  • DropDownList must not be in the update panel to cause the problem.  It is enough to change its Items collection during asynchronous postback.

  • Do you know other ways to get this exception with Microsoft ASP.NET AJAX Extensions?

10 comments:

Neeraj Yadav said...

hello Yuriy Solodkyy

thanks, Its really helpful

Anonymous said...

Thanks for the great article. It tells me exactly what I am looking for.
Vipul Shah
www.egenietech.com

MEMark said...

Your steps on how to locate the problem was excellent. Thank you very much!

/M

MEMark said...

I might add that my problem was a bit unexpected. I had a page with an updatepanel containing some controls and a hidden field outside of it. The hidden field had viewstate disabled, and therefore lost its value on partial postback in the updatepanel. When the controls posted back a second time, event validation failed for the hidden field!
/M

Nikks said...

Hello Memark

I am also getting a very unusual error. Please see if you can help me in debugging this.
I have a page with an updatepanel contol which contains a dropdown user control. On partial postbak the "Page Title" changes from "Website®" to "Website®"....It is not taking the special character symbol after post back.
What could be the problem in my code.
I would appreciate if ou can guide me.
Thanks

Anonymous said...

Your insight into this problem and clear explanation is superb! Great job and thanks for your help!

Ryan G.

Anonymous said...

Thank you so much. You just saved me hours of work!

Anonymous said...

The pictures (steps) are not showing... dead links.

梦中林 said...
This comment has been removed by a blog administrator.
Yuriy Solodkyy said...

unfortunately, I lost all the images from this blog. Basically, what you need to do is to ensure that no debugger settngs prevent displaying exceptions that are thrown not in your code.