From 55f06a7ca6a5d1b9de563aea00e6f9832a7f1249 Mon Sep 17 00:00:00 2001 From: Floris Robbemont Date: Sat, 16 Feb 2013 16:00:28 -0100 Subject: [PATCH] Fixed issue: #U4-1726 --- .../Mvc/IFilteredControllerFactory.cs | 9 ++ .../Mvc/MasterControllerFactory.cs | 17 ++++ .../Mvc/RenderControllerFactory.cs | 2 +- src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 95 ++++++++++--------- src/Umbraco.Web/Mvc/RouteDefinition.cs | 7 ++ src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs | 57 +++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 7 files changed, 142 insertions(+), 46 deletions(-) create mode 100644 src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs diff --git a/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs b/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs index 9fbb3ac34c..e6697103ad 100644 --- a/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Web.Mvc; using System.Web.Routing; @@ -13,5 +14,13 @@ namespace Umbraco.Web.Mvc /// true if this instance can handle the specified request; otherwise, false. /// bool CanHandle(RequestContext request); + + /// + /// Returns the controller type for the controller name otherwise null if not found + /// + /// + /// + /// + Type GetControllerType(RequestContext requestContext, string controllerName); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs index 357dbf899c..1f4d4b0a5f 100644 --- a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs @@ -45,6 +45,23 @@ namespace Umbraco.Web.Mvc : base.CreateController(requestContext, controllerName); } + /// + /// Retrieves the controller type for the specified name and request context. + /// + /// + /// + /// The controller type. + /// + /// The context of the HTTP request, which includes the HTTP context and route data. + /// The name of the controller. + internal Type GetControllerTypeInternal(RequestContext requestContext, string controllerName) + { + var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext)); + return factory != null + ? factory.GetControllerType(requestContext, controllerName) + : base.GetControllerType(requestContext, controllerName); + } + /// /// Releases the specified controller. /// diff --git a/src/Umbraco.Web/Mvc/RenderControllerFactory.cs b/src/Umbraco.Web/Mvc/RenderControllerFactory.cs index 56270ea36e..770c57c3bb 100644 --- a/src/Umbraco.Web/Mvc/RenderControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/RenderControllerFactory.cs @@ -42,7 +42,7 @@ namespace Umbraco.Web.Mvc /// /// /// - protected Type GetControllerType(RequestContext requestContext, string controllerName) + public Type GetControllerType(RequestContext requestContext, string controllerName) { return _innerFactory.GetControllerType(requestContext, controllerName); } diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 18f88ad54f..a0c943745c 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -4,6 +4,7 @@ using System.Text; using System.Web; using System.Web.Mvc; using System.Web.Routing; +using System.Web.SessionState; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Configuration; @@ -184,7 +185,7 @@ namespace Umbraco.Web.Mvc requestContext.RouteData.Values["action"] = postedInfo.ActionName; requestContext.RouteData.DataTokens["area"] = postedInfo.Area; - IHttpHandler handler = new MvcHandler(requestContext); + IHttpHandler handler = new UmbracoMvcHandler(requestContext); //ensure the controllerType is set if found, meaning it is a plugin, not locally declared if (postedInfo.Area != standardArea) @@ -232,57 +233,53 @@ namespace Umbraco.Web.Mvc var def = new RouteDefinition { ControllerName = defaultControllerName, - Controller = new RenderMvcController(UmbracoContext), + ControllerType = typeof(RenderMvcController), PublishedContentRequest = publishedContentRequest, ActionName = ((Route)requestContext.RouteData.Route).Defaults["action"].ToString(), HasHijackedRoute = false }; + //check that a template is defined), if it doesn't and there is a hijacked route it will just route + // to the index Action + if (publishedContentRequest.HasTemplate) + { + //the template Alias should always be already saved with a safe name. + //if there are hyphens in the name and there is a hijacked route, then the Action will need to be attributed + // with the action name attribute. + var templateName = publishedContentRequest.TemplateAlias.Split('.')[0].ToSafeAlias(); + def.ActionName = templateName; + } + //check if there's a custom controller assigned, base on the document type alias. - var controller = _controllerFactory.CreateController(requestContext, publishedContentRequest.PublishedContent.DocumentTypeAlias); - - + var controllerType = ((MasterControllerFactory)_controllerFactory).GetControllerTypeInternal(requestContext, publishedContentRequest.PublishedContent.DocumentTypeAlias); + //check if that controller exists - if (controller != null) - { + if (controllerType != null) + { + //ensure the controller is of type 'RenderMvcController' + if (controllerType.IsSubclassOf(typeof (RenderMvcController))) + { + //set the controller and name to the custom one + def.ControllerType = controllerType; + def.ControllerName = ControllerExtensions.GetControllerName(controllerType); + if (def.ControllerName != defaultControllerName) + { + def.HasHijackedRoute = true; + } + } + else + { + LogHelper.Warn( + "The current Document Type {0} matches a locally declared controller of type {1}. Custom Controllers for Umbraco routing must inherit from '{2}'.", + () => publishedContentRequest.PublishedContent.DocumentTypeAlias, + () => controllerType.FullName, + () => typeof (RenderMvcController).FullName); + //exit as we cannnot route to the custom controller, just route to the standard one. + return def; + } + } - //ensure the controller is of type 'RenderMvcController' - if (controller is RenderMvcController) - { - //set the controller and name to the custom one - def.Controller = (ControllerBase)controller; - def.ControllerName = ControllerExtensions.GetControllerName(controller.GetType()); - if (def.ControllerName != defaultControllerName) - { - def.HasHijackedRoute = true; - } - } - else - { - LogHelper.Warn( - "The current Document Type {0} matches a locally declared controller of type {1}. Custom Controllers for Umbraco routing must inherit from '{2}'.", - () => publishedContentRequest.PublishedContent.DocumentTypeAlias, - () => controller.GetType().FullName, - () => typeof(RenderMvcController).FullName); - //exit as we cannnot route to the custom controller, just route to the standard one. - return def; - } - - //check that a template is defined), if it doesn't and there is a hijacked route it will just route - // to the index Action - if (publishedContentRequest.HasTemplate) - { - //the template Alias should always be already saved with a safe name. - //if there are hyphens in the name and there is a hijacked route, then the Action will need to be attributed - // with the action name attribute. - var templateName = publishedContentRequest.TemplateAlias.Split('.')[0].ToSafeAlias(); - def.ActionName = templateName; - } - - } - - - return def; + return def; } internal IHttpHandler GetHandlerOnMissingTemplate(PublishedContentRequest pcr) @@ -365,11 +362,19 @@ namespace Umbraco.Web.Mvc requestContext.RouteData.Values["action"] = routeDef.ActionName; } + // Set the session state requirements + requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext, routeDef.ControllerName)); + // reset the friendly path so in the controllers and anything occuring after this point in time, //the URL is reset back to the original request. requestContext.HttpContext.RewritePath(UmbracoContext.OriginalRequestUrl.PathAndQuery); - return new MvcHandler(requestContext); + return new UmbracoMvcHandler(requestContext); } + + 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/RouteDefinition.cs b/src/Umbraco.Web/Mvc/RouteDefinition.cs index e9b7ffe378..49ba64fac5 100644 --- a/src/Umbraco.Web/Mvc/RouteDefinition.cs +++ b/src/Umbraco.Web/Mvc/RouteDefinition.cs @@ -1,3 +1,4 @@ +using System; using System.Web.Mvc; using Umbraco.Web.Routing; @@ -16,6 +17,12 @@ namespace Umbraco.Web.Mvc /// public ControllerBase Controller { get; set; } + /// + /// The Controller type found for routing to + /// + public Type ControllerType { get; set; } + + /// /// The current RenderModel found for the request /// diff --git a/src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs b/src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs new file mode 100644 index 0000000000..1ace297bfd --- /dev/null +++ b/src/Umbraco.Web/Mvc/UmbracoMvcHandler.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; + +namespace Umbraco.Web.Mvc +{ + /// + /// Mvc handler class to intercept creation of controller and store it for later use. + /// This means we create two instances of the same controller to support some features later on. + /// + /// The alternate option for this is to completely rewrite all MvcHandler methods. + /// + /// This is currently needed for the 'return CurrentUmbracoPage()' surface controller functionality + /// because it needs to send data back to the page controller. + /// + internal class UmbracoMvcHandler : MvcHandler + { + public UmbracoMvcHandler(RequestContext requestContext) + : base(requestContext) + { + } + + private void StoreControllerInRouteDefinition() + { + var routeDef = (RouteDefinition)RequestContext.RouteData.DataTokens["umbraco-route-def"]; + + if (routeDef == null) return; + + // Get the factory and controller and create a new instance of the controller + var factory = ControllerBuilder.Current.GetControllerFactory(); + var controller = factory.CreateController(RequestContext, routeDef.ControllerName) as ControllerBase; + + // Store the controller + routeDef.Controller = controller; + } + + protected override void ProcessRequest(HttpContextBase httpContext) + { + StoreControllerInRouteDefinition(); + + // Let MVC do its magic and continue the request + base.ProcessRequest(httpContext); + } + + protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, + object state) + { + StoreControllerInRouteDefinition(); + + return base.BeginProcessRequest(httpContext, callback, state); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a09aa5ccb7..ef55c9055f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -303,6 +303,7 @@ +