Event Validation Errors and Network Congestion in ASP.NET
Joseph Rattz emailed me about posts I wrote last year covering ASP.NET event validation (see ASP.NET Event Validation
and "Invalid Callback Or Postback Argument" Part
I and Part II). In the posts I describe one
scenario where an event validation exception can appear, and describe how to prevent the exception. In the scenario, client
side script dynamically adds values to a list (select) control. When the user POSTs the form to the server, ASP.NET sees a
new value and concludes something is wrong. The list comes back to the server with a value that wasn't present when the
list left the server. ASP.NET tracks the legal values by serializing them into a hidden input control with an ID of
__EVENTVALIDATION. This event validation feature helps to prevent all sorts of form spoofing attacks.
Joe's scenario was odd, not only because the invalid postback argument exception appeared sporadically, but because
the source of the exception was a TextBox control!
ArgumentException: Invalid postback or callback argument. ...
System.Web.UI.ClientScriptManager.ValidateEvent(..) ...
System.Web.UI.Control.ValidateEvent(..) ...
System.Web.UI.WebControls.TextBox.LoadPostData(..) ...
What could ASP.NET possibly validate about a TextBox? A server can expect specific values from a DropDownList, but
a plain TextBox allows free form text entry by the user. The answer (via Reflector), is that the TextBox control only
validates that its UniqueID is present in the legal arguments described by the __EVENTVALIDATION data. Even after knowing
this information, it's hard to see what could go possibly wrong with a TextBox.
Joe's theory, which I believe to be correct, is that the user might create a postback before their browser
even receives the __EVENTVALIDATION form input. This could happen, for example, over a poor connection. The resulting POST
won't contain the __EVENTVALIDATION input, and thus ASP.NET cannot validate the postback arguments. The klaxons wail. Glass
breaks. The runtime throws an exception.
One way to validate this theory is to simulate network congestion with the following control. We flush out the
output stream, sleep, and then continue rendering.
using System;
using System.Web.UI;
using
System.Threading;
namespace OTC
{
public
class
FlushAndSleep : Control
{
protected
override
void Render(HtmlTextWriter writer)
{
writer.Write
("Begin Render<br>");
base.Render
(writer);
Page.Response.Flush();
Thread.Sleep(2000);
writer.Write("End Render<br>");
}
}
}
Stick the control into the following page:
<%@
Page
Language="C#" %>
<%@
Register
TagPrefix="otc"
Assembly="App_Code"
Namespace="OTC" %>
<form
id="form1"
runat="server">
<div>
<asp:textbox
runat="server"
id="TextBox1"
/>
<asp:button
runat="server"
id="Button1"
text="Submit"
/>
<br
/>
<otc:FlushAndSleep
runat="server"
ID="FlushAndSleep"
/>
</div>
</form>
Run the page and click the button as soon as it appears in the browser. Voila! Instant "invalid callback or
postback argument" exception! If you ever see the exception occur sporadically, this might be the reason.