In my unwanted quest to port a MonoRail app to ASP.NET MVC, I have to use existing master pages and user controls.
Bad news is, some of these user controls post back and then all hell breaks loose. Specifically, I started getting this exception on every postback:
Line 338: <% if (Model.PaymentOptions.Count > 0) {%>
Line 339: <div class="fleft" style="margin:2px 20px 2px 0px; padding: 0px;">
Line 340: <% Html.RenderPartial("ListingDetailPaymentOption", Model.PaymentOptions[0]);%>
Line 341: </div>
Line 342: <% }%>
[HttpException]: Unable to validate data.
at System.Web.Configuration.MachineKeySection.GetDecodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32 length, Int32& dataLength)
at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
[ViewStateException]: Invalid viewstate.
Client IP: 127.0.0.1
Port: 18559
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6 (.NET CLR 3.5.30729)
ViewState: /wEPDwUKLTc0NTAwNTg1NA9kFgJmD2QWAmYPZBYCZg9kFgICBw9kFgICBQ9kFgICAw9kFgICAw9kFgICAg9kFgJmDxYCHgtfIUl0ZW1Db3VudAIFFgpmD2QWAmYPFQk8LzIwMDZfTGFuZF9Sb3Zlcl9SYW5nZV9Sb3Zlcl9TcG9ydF9TdXBlcmNoYXJnZWRfMTEyNjg3LnhodG1sBDIwMDYpTGFuZCBSb3ZlciBSYW5nZSBSb3ZlciBTcG9ydCBTdXBlcmNoYXJnZWQ1L3Bob3Rvcy9pbWdzaG93Uy5hc3B4P2lkPTI0MTgmc2l6ZT10XzEyMHg5MCZZZWFyPTIwMDY8LzIwMDZfTGFuZF9Sb3Zlcl9SYW5nZV9Sb3Zlcl9TcG9ydF9TdXBlcmNoYXJnZWRfMTEyNjg3LnhodG1sBDIwMDYpTGFuZCBSb3ZlciBSYW5nZSBSb3ZlciBTcG9ydCBTdXBlcmNoYXJnZWQCMTkHJDk1MC4wMGQCAQ9kFgJmDxUJJy8yMDA2X01lcmNlZGVzX0UzNTBfU2VkYW5fXzExMjk4MC54aHRtbAQyMDA2FE1lcmNlZGVzIEUzNTAgU2VkYW4gKy9waG90b3MvaW1nc2hvdy5hc3B4P2lkPTY4MjE4JnNpemU9dF8xMjB4OTAnLzIwMDZfTWVyY2VkZXNfRTM1MF9TZWRhbl9fMTEyOTgwLnhodG1sBDIwMDYUTWVyY2VkZXMgRTM1MCBTZWRhbiACMTIHJDQ4OC4wMGQCAg9kFgJmDxUJLy8yMDA2X01lcmNlZGVzX0NMUzUwMF9Db3VwZV80X0Rvb3JfMTA4NjI4LnhodG1s...
[HttpException]: Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.
at System.Web.UI.ViewStateException.ThrowError(Exception inner, String persistedState, String errorPageMessage, Boolean macValidationError)
at System.Web.UI.ViewStateException.ThrowMacValidationError(Exception inner, String persistedState)
at System.Web.UI.ObjectStateFormatter.Deserialize(String inputString)
at System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter.Deserialize(String serializedState)
at System.Web.UI.Util.DeserializeWithAssert(IStateFormatter formatter, String serializedState)
at System.Web.UI.HiddenFieldPageStatePersister.Load()
at System.Web.UI.Page.LoadPageStateFromPersistenceMedium()
at System.Web.UI.Page.LoadAllState()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest()
at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
at System.Web.UI.Page.ProcessRequest(HttpContext context)
at System.Web.Mvc.ViewPage.RenderView(ViewContext viewContext)
at System.Web.Mvc.ViewUserControl.RenderViewAndRestoreContentType(ViewPage containerPage, ViewContext viewContext)
at System.Web.Mvc.ViewUserControl.RenderView(ViewContext viewContext)
at System.Web.Mvc.WebFormView.RenderViewUserControl(ViewContext context, ViewUserControl control)
at System.Web.Mvc.WebFormView.Render(ViewContext viewContext, TextWriter writer)
at System.Web.Mvc.HtmlHelper.RenderPartialInternal(String partialViewName, ViewDataDictionary viewData, Object model, ViewEngineCollection viewEngineCollection)
at System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper htmlHelper, String partialViewName, Object model)
at ASP.views_listingdetail_index_aspx.__Render__control2(HtmlTextWriter __w, Control parameterContainer) in c:\trabajo\web\WEB2\Views\ListingDetail\Index.aspx:line 340
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.Control.Render(HtmlTextWriter writer)
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.Control.Render(HtmlTextWriter writer)
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.Control.Render(HtmlTextWriter writer)
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.HtmlControls.HtmlForm.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.HtmlControls.HtmlContainerControl.Render(HtmlTextWriter writer)
at System.Web.UI.HtmlControls.HtmlForm.Render(HtmlTextWriter output)
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.HtmlControls.HtmlForm.RenderControl(HtmlTextWriter writer)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.Control.Render(HtmlTextWriter writer)
at Templates.master.Render(HtmlTextWriter writer) in C:\trabajo\web\WEB2\templates\master.master.vb:line 29
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.Control.Render(HtmlTextWriter writer)
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.Control.Render(HtmlTextWriter writer)
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
at System.Web.UI.Control.RenderChildren(HtmlTextWriter writer)
at System.Web.UI.Page.Render(HtmlTextWriter writer)
at System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer)
at System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter)
at System.Web.UI.Control.RenderControl(HtmlTextWriter writer)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
at System.Web.UI.Page.ProcessRequest()
at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
at System.Web.UI.Page.ProcessRequest(HttpContext context)
at ASP.views_listingdetail_index_aspx.ProcessRequest(HttpContext context) in c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\ffa83e12\a51f62df\App_Web_index.aspx.1cc8d514.vtl-v5gs.0.cs:line 0
at System.Web.Mvc.ViewPage.RenderView(ViewContext viewContext)
at System.Web.Mvc.WebFormView.RenderViewPage(ViewContext context, ViewPage page)
at System.Web.Mvc.WebFormView.Render(ViewContext viewContext, TextWriter writer)
at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass11.b__e()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass11.<>c__DisplayClass13.b__10()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
at System.Web.Mvc.Controller.ExecuteCore()
at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext)
at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext)
at System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
As usual, first thing I did was googling the exception, but the standard answer was: "Postback is not supported in MVC". I also found this Microsoft Connect issue that confirmed that this is a bug.
What I found weird is that the exception was actually throwing in the ViewUserControl (MVC's user control), not the legacy WebForms user controls. I took a look at the source of ViewUserControl.RenderView() and found that the rendering is actually done by creating a fake ViewPage, and that the ViewState validation in this ViewPage was the one failing. But, since in MVC we don't use ViewState, we can just turn it off. So I created this little base class to do that, just inherit from this one instead of ViewUserControl<T>:
public class ViewUserControlWithoutViewState<T> : ViewUserControl<T> where T : class {
protected override void LoadViewState(object savedState) {}
protected override object SaveControlState() {
return null;
}
protected override void LoadControlState(object savedState) {}
protected override object SaveViewState() {
return null;
}
/// <summary>
/// extracted from System.Web.Mvc.ViewUserControl
/// </summary>
/// <param name="viewContext"></param>
public override void RenderView(ViewContext viewContext) {
viewContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);
var containerPage = new ViewUserControlContainerPage(this);
ID = Guid.NewGuid().ToString();
RenderViewAndRestoreContentType(containerPage, viewContext);
}
/// <summary>
/// extracted from System.Web.Mvc.ViewUserControl
/// </summary>
/// <param name="containerPage"></param>
/// <param name="viewContext"></param>
public static void RenderViewAndRestoreContentType(ViewPage containerPage, ViewContext viewContext) {
string contentType = viewContext.HttpContext.Response.ContentType;
containerPage.RenderView(viewContext);
viewContext.HttpContext.Response.ContentType = contentType;
}
/// <summary>
/// Extracted from System.Web.Mvc.ViewUserControl+ViewUserControlContainerPage
/// </summary>
private sealed class ViewUserControlContainerPage : ViewPage {
// Methods
public ViewUserControlContainerPage(ViewUserControl userControl) {
Controls.Add(userControl);
EnableViewState = false;
}
protected override object LoadPageStateFromPersistenceMedium() {
return null;
}
protected override void SavePageStateToPersistenceMedium(object state) {}
}
}
DISCLAIMER: it worked for my specific case, I'm not claiming this covers all possible cases of this exception! Hopefully this problem will be addressed soon by
Phil et al...