diff --git a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs
index aa88de9348..13267dd241 100644
--- a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs
+++ b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs
@@ -118,19 +118,15 @@ namespace Umbraco.Web.Macros
routeVals.Values.Add("action", "Index");
routeVals.DataTokens.Add("umbraco-context", umbCtx); //required for UmbracoViewPage
- //lets render this controller as a child action if we are currently executing using MVC
- //(otherwise don't do this since we're using webforms)
- var mvcHandler = http.CurrentHandler as MvcHandler;
+ //lets render this controller as a child action
var viewContext = new ViewContext {ViewData = new ViewDataDictionary()};;
- if (mvcHandler != null)
- {
- //try and extract the current view context from the route values, this would be set in the UmbracoViewPage.
- if (mvcHandler.RequestContext.RouteData.DataTokens.ContainsKey(Umbraco.Web.Mvc.Constants.DataTokenCurrentViewContext))
- {
- viewContext = (ViewContext) mvcHandler.RequestContext.RouteData.DataTokens[Umbraco.Web.Mvc.Constants.DataTokenCurrentViewContext];
- }
- routeVals.DataTokens.Add("ParentActionViewContext", viewContext);
- }
+ //try and extract the current view context from the route values, this would be set in the UmbracoViewPage or in
+ // the UmbracoPageResult if POSTing to an MVC controller but rendering in Webforms
+ if (http.Request.RequestContext.RouteData.DataTokens.ContainsKey(Mvc.Constants.DataTokenCurrentViewContext))
+ {
+ viewContext = (ViewContext)http.Request.RequestContext.RouteData.DataTokens[Mvc.Constants.DataTokenCurrentViewContext];
+ }
+ routeVals.DataTokens.Add("ParentActionViewContext", viewContext);
var request = new RequestContext(http, routeVals);
string output;
diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
index bfa2063f7a..04ae377027 100644
--- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
+++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
@@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Text;
using System.Web;
+using System.Web.Compilation;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
@@ -359,8 +360,9 @@ namespace Umbraco.Web.Mvc
// so we have a template, so we should have a rendering engine
if (pcr.RenderingEngine == RenderingEngine.WebForms) // back to webforms ?
- return (global::umbraco.UmbracoDefault)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault));
- else if (pcr.RenderingEngine != RenderingEngine.Mvc) // else ?
+ return GetWebFormsHandler();
+
+ if (pcr.RenderingEngine != RenderingEngine.Mvc) // else ?
return new PublishedContentNotFoundHandler("In addition, no rendering engine exists to render the custom 404.");
return null;
@@ -385,6 +387,14 @@ namespace Umbraco.Web.Mvc
return HandlePostedValues(requestContext, postedInfo);
}
+ //Now we can check if we are supposed to render WebForms when the route has not been hijacked
+ if (publishedContentRequest.RenderingEngine == RenderingEngine.WebForms
+ && publishedContentRequest.HasTemplate
+ && routeDef.HasHijackedRoute == false)
+ {
+ return GetWebFormsHandler();
+ }
+
//here we need to check if there is no hijacked route and no template assigned, if this is the case
//we want to return a blank page, but we'll leave that up to the NoTemplateHandler.
if (!publishedContentRequest.HasTemplate && !routeDef.HasHijackedRoute)
@@ -435,9 +445,20 @@ namespace Umbraco.Web.Mvc
return new UmbracoMvcHandler(requestContext);
}
+ ///
+ /// Returns the handler for webforms requests
+ ///
+ ///
+ internal static IHttpHandler GetWebFormsHandler()
+ {
+ return (global::umbraco.UmbracoDefault)BuildManager.CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault));
+ }
+
private SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext, string controllerName)
{
return _controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
}
+
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/UmbracoPageResult.cs b/src/Umbraco.Web/Mvc/UmbracoPageResult.cs
index f0904c64a4..d6fe6964f6 100644
--- a/src/Umbraco.Web/Mvc/UmbracoPageResult.cs
+++ b/src/Umbraco.Web/Mvc/UmbracoPageResult.cs
@@ -1,7 +1,10 @@
using System;
+using System.IO;
+using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
+using umbraco.presentation.install.utills;
namespace Umbraco.Web.Mvc
{
@@ -17,23 +20,33 @@ namespace Umbraco.Web.Mvc
ValidateRouteData(context.RouteData);
var routeDef = (RouteDefinition)context.RouteData.DataTokens["umbraco-route-def"];
- var factory = ControllerBuilder.Current.GetControllerFactory();
- context.RouteData.Values["action"] = routeDef.ActionName;
-
- ControllerBase controller = null;
-
- try
+ //Special case, if it is webforms but we're posting to an MVC surface controller, then we
+ // need to return the webforms result instead
+ if (routeDef.PublishedContentRequest.RenderingEngine == RenderingEngine.WebForms)
{
- controller = CreateController(context, factory, routeDef);
-
- CopyControllerData(context, controller);
-
- ExecuteControllerAction(context, controller);
+ EnsureViewContextForWebForms(context);
+ var webFormsHandler = RenderRouteHandler.GetWebFormsHandler();
+ webFormsHandler.ProcessRequest(HttpContext.Current);
}
- finally
+ else
{
- CleanupController(controller, factory);
+ var factory = ControllerBuilder.Current.GetControllerFactory();
+ context.RouteData.Values["action"] = routeDef.ActionName;
+ ControllerBase controller = null;
+
+ try
+ {
+ controller = CreateController(context, factory, routeDef);
+
+ CopyControllerData(context, controller);
+
+ ExecuteControllerAction(context, controller);
+ }
+ finally
+ {
+ CleanupController(controller, factory);
+ }
}
}
@@ -70,6 +83,27 @@ namespace Umbraco.Web.Mvc
}
}
+ ///
+ /// When POSTing to MVC but rendering in WebForms we need to do some trickery, we'll create a dummy viewcontext with all of the
+ /// current modelstate, tempdata, viewdata so that if we're rendering partial view macros within the webforms view, they will
+ /// get all of this merged into them.
+ ///
+ ///
+ private static void EnsureViewContextForWebForms(ControllerContext context)
+ {
+ var tempDataDictionary = new TempDataDictionary();
+ tempDataDictionary.Save(context, new SessionStateTempDataProvider());
+ var viewCtx = new ViewContext(context, new DummyView(), new ViewDataDictionary(), tempDataDictionary, new StringWriter());
+
+ viewCtx.ViewData.ModelState.Merge(context.Controller.ViewData.ModelState);
+
+ foreach (var d in context.Controller.ViewData)
+ viewCtx.ViewData[d.Key] = d.Value;
+
+ //now we need to add it to the special route tokens so it's picked up later
+ context.HttpContext.Request.RequestContext.RouteData.DataTokens[Constants.DataTokenCurrentViewContext] = viewCtx;
+ }
+
///
/// Ensure ModelState, ViewData and TempData is copied across
///
@@ -120,5 +154,12 @@ namespace Umbraco.Web.Mvc
if (controller != null)
controller.DisposeIfDisposable();
}
+
+ private class DummyView : IView
+ {
+ public void Render(ViewContext viewContext, TextWriter writer)
+ {
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs
index 8f7e5fc208..3a616e22bc 100644
--- a/src/Umbraco.Web/UmbracoModule.cs
+++ b/src/Umbraco.Web/UmbracoModule.cs
@@ -395,7 +395,8 @@ namespace Umbraco.Web
#endregion
///
- /// Rewrites to the correct Umbraco handler, either WebForms or Mvc
+ /// Rewrites to the Umbraco handler - we always send the request via our MVC rendering engine, this will deal with
+ /// requests destined for webforms.
///
///
///
@@ -406,49 +407,24 @@ namespace Umbraco.Web
// rewritten url, but this is not what we want!
// read: http://forums.iis.net/t/1146511.aspx
- string query = pcr.Uri.Query.TrimStart(new[] { '?' });
+ var query = pcr.Uri.Query.TrimStart(new[] { '?' });
- string rewritePath;
+ // GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any)
+ var rewritePath = GlobalSettings.Path.TrimEnd(new[] { '/' }) + "/RenderMvc";
+ // rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc)
+ context.RewritePath(rewritePath, "", query, false);
- if (pcr.RenderingEngine == RenderingEngine.Unknown)
- {
- // Unkwnown means that no template was found. Default to Mvc because Mvc supports hijacking
- // routes which sometimes doesn't require a template since the developer may want full control
- // over the rendering. Can't do it in WebForms, so Mvc it is. And Mvc will also handle what to
- // do if no template or hijacked route is exist.
- pcr.RenderingEngine = RenderingEngine.Mvc;
- }
-
- switch (pcr.RenderingEngine)
- {
- case RenderingEngine.Mvc:
- // GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any)
- rewritePath = GlobalSettings.Path.TrimEnd(new[] { '/' }) + "/RenderMvc";
- // rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc)
- context.RewritePath(rewritePath, "", query, false);
-
- //if it is MVC we need to do something special, we are not using TransferRequest as this will
- //require us to rewrite the path with query strings and then reparse the query strings, this would
- //also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create
- //an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does:
- // * Looks up the route based on the new rewritten URL
- // * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route
- //we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal
- //so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink
- //a bunch of things!
- var urlRouting = new UrlRoutingModule();
- urlRouting.PostResolveRequestCache(context);
- break;
-
- case RenderingEngine.WebForms:
- rewritePath = "~/default.aspx";
- // rewrite the path to the path of the handler (i.e. default.aspx)
- context.RewritePath(rewritePath, "", query, false);
- break;
-
- default:
- throw new Exception("Invalid RenderingEngine.");
- }
+ //if it is MVC we need to do something special, we are not using TransferRequest as this will
+ //require us to rewrite the path with query strings and then reparse the query strings, this would
+ //also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create
+ //an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does:
+ // * Looks up the route based on the new rewritten URL
+ // * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route
+ //we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal
+ //so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink
+ //a bunch of things!
+ var urlRouting = new UrlRoutingModule();
+ urlRouting.PostResolveRequestCache(context);
}
///
diff --git a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs
index e4d7c546d6..c7e4cd5eda 100644
--- a/src/Umbraco.Web/umbraco.presentation/default.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/default.aspx.cs
@@ -1,6 +1,7 @@
using System;
using System.Threading;
using System.Web;
+using System.Web.Mvc;
using System.Web.Routing;
using System.Web.UI;
using System.IO;
@@ -29,6 +30,13 @@ namespace umbraco
///
public class UmbracoDefault : Page
{
+ ///
+ /// Simply used to clear temp data
+ ///
+ private class TempDataController : Controller
+ {
+ }
+
private page _upage;
private PublishedContentRequest _docRequest;
bool _validateRequest = true;
@@ -85,9 +93,19 @@ namespace umbraco
{
using (DisposableTimer.DebugDuration("Init"))
{
-
base.OnInit(e);
+ //This is a special case for webforms since in some cases we may be POSTing to an MVC controller, adding TempData there and then redirecting
+ // to a webforms handler. In that case we need to manually clear out the tempdata ourselves since this is normally the function of the base
+ // MVC controller instance and since that is not executing, we'll deal with that here.
+ //Unfortunately for us though, we can never know which TempDataProvider was used for the previous controller, by default it is the sessionstateprovider
+ // but since the tempdataprovider is not a global mvc thing, it is only a per-controller thing, we can only just assume it will be the sessionstateprovider
+ var provider = new SessionStateTempDataProvider();
+ //We create a custom controller context, the only thing that is referenced from this controller context in the sessionstateprovider is the HttpContext.Session
+ // so we just need to ensure that is set
+ var ctx = new ControllerContext(new HttpContextWrapper(Context), new RouteData(), new TempDataController());
+ provider.LoadTempData(ctx);
+
//This is only here for legacy if people arent' using master pages...
//TODO: We need to test that this still works!! Or do we ??
if (!UmbracoSettings.UseAspNetMasterPages)