Showing posts with label ASP.NET Bugs. Show all posts
Showing posts with label ASP.NET Bugs. Show all posts

Wednesday, 30 April 2008

Validation of viewstate MAC failed error - Fixed

There is a problem with ASP.NET encrypted viewstate which means it will generate an error if your page is slow to load and the user tries to post back to the page (e.g. by clicking a button). Encrypted viewstate typically exists on the page because any controls (like GridViews) that use "DataKeyNames" require it. The big problem is that it is put at the bottom of the page just before the end form tag - which may not have been loaded by the time a user post backs on a slow-loading page. This often happens on the first hit to a page as the client is downloading images or the JIT compiler is doing its work.

One of the best articles I've seen on some of the solutions to this problem is here:

http://blogs.msdn.com/tom/archive/2008/03/14/validation-of-viewstate-mac-failed-error.aspx

There are 3 workarounds suggested:
  1. Set enableEventValidation to false and viewStateEncryptionMode to Never.
  2. Disable the form until the page has finished loading
    function enableForm() {
    document.getElementById("form").disabled = false;
    }
    window.onLoad = enableForm();


  3. Or override the Render of your page so that the Encrypted section of the viewstate is put at the top of the form rather than at the bottom as it is by default. I've tested this version in our application which uses Virtual Earth maps and it fixes the issues with have with slow page loads (RECOMMENDED as it does not rely on javascript to work):




public abstract class BasePage : Framework.Web.UI.Pages.BasePage
{
/// <summary>
/// Fix for viewstate issue when the user clicks on a button before the encrypted section
/// of the viewstate is finished.
/// As per fix in
/// http://blogs.msdn.com/tom/archive/2008/03/14/validation-of-viewstate-mac-failed-error.aspx
/// </summary>
/// <param name="writer"></param>
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
System.IO.StringWriter stringWriter = new System.IO.StringWriter();
HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);
base.Render(htmlWriter);
string html = stringWriter.ToString();
string[] aspnet_formelems = new string[5];
aspnet_formelems[0] = "__EVENTTARGET";
aspnet_formelems[1] = "__EVENTARGUMENT";
aspnet_formelems[2] = "__VIEWSTATE";
aspnet_formelems[3] = "__EVENTVALIDATION"; aspnet_formelems[4] = "__VIEWSTATEENCRYPTED";
foreach (string elem in aspnet_formelems)
{
//Response.Write("input type=""hidden"" name=""" & abc.ToString & """")
int StartPoint = html.IndexOf("<input type=\"hidden\" name=\"" +
elem.ToString() + "\"");
if (StartPoint >= 0)
{
//does __VIEWSTATE exist?
int EndPoint = html.IndexOf("/>", StartPoint) + 2;
string ViewStateInput = html.Substring(StartPoint, EndPoint - StartPoint);
html = html.Remove(StartPoint, EndPoint - StartPoint);
int FormStart = html.IndexOf("<form");
int EndForm = html.IndexOf(">", FormStart) + 1;
if (EndForm >= 0)
html = html.Insert(EndForm, ViewStateInput);
}
}

writer.Write(html);
}

Thursday, 14 February 2008

ASP.NET double-postback bug strikes again!

A bug in ASP.NET has returned from the dead to haunt me today. I sat with one of my colleagues for over an hour to try and work out the issue to no avail. The problem was that the first Save worked fine - but I kept getting Optimistic Concurrency data errors when I saved the record for the second time (never the first). The big problem with ASP.NET bugs is that you tend to look in your code for the problem - that single attribute that shouldn't be there, the new code in our custom ObjectContainer data source or translation layer, that line of code that nullifies the value, that AJAX control not set up correctly. But there was none. After stepping through the code line by line, removing any Telerik Ajax Managers and Postback Panels -and anything remotely suspicious at - the problem was still there. After a bit more Divide and Konquering (DK for short) with CTL+E, CTL+C (the VS Shortcut for commenting out code), I finally found my old nemesis of the single empty image tag (which still exists in ASP.NET 3.5).

THE BUG:
An example of this bug is detailed here:
http://www.velocityreviews.com/forums/t119525-repost-gridview-imagebutton-causes-double-postback.html

It is also detailed here:
http://www.dotnetspider.com/qa/Question8706.aspx

The issue occurs if you have any image tags rendered by your controls which have an empty source. As soon as the browser hits that <img src=""/> tag it does a refresh of the page. The main problem with this double postback is that the second run is NOT a postback - the Page.IsPostBack property is false - but I still have all my viewstate values. This issue is incredibly frustrating as the natural inclination is to look at controls causing partial postbacks - but you'd be looking in the wrong place. I had this issue in IE 6,7 and Firefox 2.

When I have some spare time, I'll look into how the problem occurs with Lutz Roeder's handy Reflector and find out who to tell to fix this reocurring issue :o)

THE FIX:
To stop the double post-backs, just make sure all your ASP.NET controls (Image Buttons, Image Columns in grids, normal Images), all have a src attribute filled in - or otherwise, make them invisible. Otherwise, you will have phantom postbacks coming to haunt you when you least need it!