From 55f06a7ca6a5d1b9de563aea00e6f9832a7f1249 Mon Sep 17 00:00:00 2001 From: Floris Robbemont Date: Sat, 16 Feb 2013 16:00:28 -0100 Subject: [PATCH 1/2] 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 @@ + From d8721197b75e0f6c9eb7ae7dc2b55badabcb6f6d Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 19 Feb 2013 07:30:46 +0600 Subject: [PATCH 2/2] Fixes up pull request so that there is no breaking changes. We now type check for a new base class called UmbracoControllerFactory to get the controller type. Adds SurfaceRouteHandler to ensure that UmbracoMvcHandler is used for all SurfaceControllers, and updates all routes accordingly. Streamlines the RenderRouteHandler to then just look up surface routes in the route table for posted values. This now ensures that UmbracoMvcHandler is used even with plugin surface controllers. --- .../Mvc/AreaRegistrationExtensions.cs | 3 +- .../Mvc/IFilteredControllerFactory.cs | 8 +- .../Mvc/MasterControllerFactory.cs | 20 +++- src/Umbraco.Web/Mvc/PluginControllerArea.cs | 4 +- .../Mvc/RenderControllerFactory.cs | 91 +------------------ src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 60 +++++++----- src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs | 16 ++++ .../Mvc/UmbracoControllerFactory.cs | 82 +++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 2 + src/Umbraco.Web/WebBootManager.cs | 4 +- 10 files changed, 165 insertions(+), 125 deletions(-) create mode 100644 src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs create mode 100644 src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs diff --git a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs index c867a0823d..cc7cbbca19 100644 --- a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs +++ b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs @@ -26,7 +26,7 @@ namespace Umbraco.Web.Mvc /// The DataToken value to set for the 'umbraco' key, this defaults to 'backoffice' /// /// - internal static void RouteControllerPlugin(this AreaRegistration area, string controllerName, Type controllerType, RouteCollection routes, + internal static Route RouteControllerPlugin(this AreaRegistration area, string controllerName, Type controllerType, RouteCollection routes, string controllerSuffixName, string defaultAction, object defaultId, string umbracoTokenValue = "backoffice") { @@ -72,6 +72,7 @@ namespace Umbraco.Web.Mvc controllerPluginRoute.DataTokens.Add("area", area.AreaName); controllerPluginRoute.DataTokens.Add("umbraco", umbracoTokenValue); //ensure the umbraco token is set + return controllerPluginRoute; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs b/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs index e6697103ad..214532b6e4 100644 --- a/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs @@ -5,6 +5,7 @@ using System.Web.Routing; namespace Umbraco.Web.Mvc { + public interface IFilteredControllerFactory : IControllerFactory { /// @@ -15,12 +16,5 @@ namespace Umbraco.Web.Mvc /// 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 1f4d4b0a5f..4a58fb7251 100644 --- a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs @@ -57,9 +57,23 @@ namespace Umbraco.Web.Mvc 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); + if (factory != null) + { + //check to see if the factory is of type UmbracoControllerFactory which exposes the GetControllerType method so we don't have to create + // an instance of the controller to figure out what it is. This is a work around for not having a breaking change for: + // http://issues.umbraco.org/issue/U4-1726 + + var umbFactory = factory as UmbracoControllerFactory; + if (umbFactory != null) + { + return umbFactory.GetControllerType(requestContext, controllerName); + } + //we have no choice but to instantiate the controller + var instance = factory.CreateController(requestContext, controllerName); + return instance.GetType(); + } + + return base.GetControllerType(requestContext, controllerName); } /// diff --git a/src/Umbraco.Web/Mvc/PluginControllerArea.cs b/src/Umbraco.Web/Mvc/PluginControllerArea.cs index 591d53480a..42652eafd4 100644 --- a/src/Umbraco.Web/Mvc/PluginControllerArea.cs +++ b/src/Umbraco.Web/Mvc/PluginControllerArea.cs @@ -67,7 +67,9 @@ namespace Umbraco.Web.Mvc { foreach (var s in surfaceControllers) { - this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "Surface", "Index", UrlParameter.Optional, "surface"); + var route = this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "Surface", "Index", UrlParameter.Optional, "surface"); + //set the route handler to our SurfaceRouteHandler + route.RouteHandler = new SurfaceRouteHandler(); } } } diff --git a/src/Umbraco.Web/Mvc/RenderControllerFactory.cs b/src/Umbraco.Web/Mvc/RenderControllerFactory.cs index 770c57c3bb..9482815a03 100644 --- a/src/Umbraco.Web/Mvc/RenderControllerFactory.cs +++ b/src/Umbraco.Web/Mvc/RenderControllerFactory.cs @@ -1,109 +1,26 @@ -using System; -using System.Web.Mvc; using System.Web.Routing; -using System.Web.SessionState; -using Umbraco.Core; namespace Umbraco.Web.Mvc { - /// + /// /// A controller factory for the render pipeline of Umbraco. This controller factory tries to create a controller with the supplied /// name, and falls back to UmbracoController if none was found. /// /// - public class RenderControllerFactory : IFilteredControllerFactory + public class RenderControllerFactory : UmbracoControllerFactory { - private readonly OverridenDefaultControllerFactory _innerFactory = new OverridenDefaultControllerFactory(); - - /// - /// Initializes a new instance of the class. - /// - public RenderControllerFactory() - { - - } - - + /// /// Determines whether this instance can handle the specified request. /// /// The request. /// true if this instance can handle the specified request; otherwise, false. /// - public virtual bool CanHandle(RequestContext request) + public override bool CanHandle(RequestContext request) { var dataToken = request.RouteData.DataTokens["area"]; return dataToken == null || string.IsNullOrWhiteSpace(dataToken.ToString()); } - /// - /// Returns the controller type for the controller name otherwise null if not found - /// - /// - /// - /// - public Type GetControllerType(RequestContext requestContext, string controllerName) - { - return _innerFactory.GetControllerType(requestContext, controllerName); - } - - /// - /// Creates the specified controller by using the specified request context. - /// - /// - /// The controller. - /// - /// The request context.The name of the controller. - public virtual IController CreateController(RequestContext requestContext, string controllerName) - { - Type controllerType = GetControllerType(requestContext, controllerName) ?? - _innerFactory.GetControllerType(requestContext, ControllerExtensions.GetControllerName(typeof(RenderMvcController))); - - return _innerFactory.GetControllerInstance(requestContext, controllerType); - } - - /// - /// Gets the controller's session behavior. - /// - /// - /// The controller's session behavior. - /// - /// The request context.The name of the controller whose session behavior you want to get. - public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) - { - return ((IControllerFactory)_innerFactory).GetControllerSessionBehavior(requestContext, controllerName); - } - - /// - /// Releases the specified controller. - /// - /// The controller. - public void ReleaseController(IController controller) - { - _innerFactory.ReleaseController(controller); - } - - /// - /// By default, only exposes which throws an exception - /// if the controller is not found. Since we want to try creating a controller, and then fall back to if one isn't found, - /// this nested class changes the visibility of 's internal methods in order to not have to rely on a try-catch. - /// - /// - internal class OverridenDefaultControllerFactory : DefaultControllerFactory - { - public new IController GetControllerInstance(RequestContext requestContext, Type controllerType) - { - return base.GetControllerInstance(requestContext, controllerType); - } - - public new Type GetControllerType(RequestContext requestContext, string controllerName) - { - if (controllerName.IsNullOrWhiteSpace()) - { - return null; - } - return base.GetControllerType(requestContext, controllerName); - } - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index a0c943745c..680d94ff79 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -185,34 +185,44 @@ namespace Umbraco.Web.Mvc requestContext.RouteData.Values["action"] = postedInfo.ActionName; requestContext.RouteData.DataTokens["area"] = postedInfo.Area; - IHttpHandler handler = new UmbracoMvcHandler(requestContext); + IHttpHandler handler; + + //get the route from the defined routes + using (RouteTable.Routes.GetReadLock()) + { + Route surfaceRoute; + if (postedInfo.Area == standardArea) + { + //find the controller in the route table without an area + surfaceRoute = RouteTable.Routes.OfType() + .SingleOrDefault(x => x.Defaults != null && + x.Defaults.ContainsKey("controller") && + x.Defaults["controller"].ToString() == postedInfo.ControllerName && + !x.DataTokens.ContainsKey("area")); + } + else + { + //find the controller in the route table with the specified area + surfaceRoute = RouteTable.Routes.OfType() + .SingleOrDefault(x => x.Defaults != null && + x.Defaults.ContainsKey("controller") && + x.Defaults["controller"].ToString() == postedInfo.ControllerName && + x.DataTokens.ContainsKey("area") && + x.DataTokens["area"].ToString() == postedInfo.Area); + } - //ensure the controllerType is set if found, meaning it is a plugin, not locally declared - if (postedInfo.Area != standardArea) - { - //requestContext.RouteData.Values["controllerType"] = postedInfo.ControllerType; - //find the other data tokens for this route and merge... things like Namespace will be included here - using (RouteTable.Routes.GetReadLock()) - { - var surfaceRoute = RouteTable.Routes.OfType() - .SingleOrDefault(x => x.Defaults != null && - x.Defaults.ContainsKey("controller") && - x.Defaults["controller"].ToString() == postedInfo.ControllerName && - x.DataTokens.ContainsKey("area") && - x.DataTokens["area"].ToString() == postedInfo.Area); - if (surfaceRoute == null) - throw new InvalidOperationException("Could not find a Surface controller route in the RouteTable for controller name " + postedInfo.ControllerName + " and within the area of " + postedInfo.Area); - //set the 'Namespaces' token so the controller factory knows where to look to construct it - if (surfaceRoute.DataTokens.ContainsKey("Namespaces")) - { - requestContext.RouteData.DataTokens["Namespaces"] = surfaceRoute.DataTokens["Namespaces"]; - } - handler = surfaceRoute.RouteHandler.GetHttpHandler(requestContext); - } + if (surfaceRoute == null) + throw new InvalidOperationException("Could not find a Surface controller route in the RouteTable for controller name " + postedInfo.ControllerName); + //set the 'Namespaces' token so the controller factory knows where to look to construct it + if (surfaceRoute.DataTokens.ContainsKey("Namespaces")) + { + requestContext.RouteData.DataTokens["Namespaces"] = surfaceRoute.DataTokens["Namespaces"]; + } + handler = surfaceRoute.RouteHandler.GetHttpHandler(requestContext); - } + } - //store the original URL this came in on + //store the original URL this came in on requestContext.RouteData.DataTokens["umbraco-item-url"] = requestContext.HttpContext.Request.Url.AbsolutePath; //store the original route definition requestContext.RouteData.DataTokens["umbraco-route-def"] = routeDefinition; diff --git a/src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs b/src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs new file mode 100644 index 0000000000..8e56cd715f --- /dev/null +++ b/src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs @@ -0,0 +1,16 @@ +using System.Web; +using System.Web.Routing; + +namespace Umbraco.Web.Mvc +{ + /// + /// Assigned to all SurfaceController's so that it returns our custom SurfaceMvcHandler to use for rendering + /// + internal class SurfaceRouteHandler : IRouteHandler + { + public IHttpHandler GetHttpHandler(RequestContext requestContext) + { + return new UmbracoMvcHandler(requestContext); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs b/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs new file mode 100644 index 0000000000..a8f3817b6c --- /dev/null +++ b/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs @@ -0,0 +1,82 @@ +using System; +using System.Web.Mvc; +using System.Web.Routing; +using System.Web.SessionState; +using Umbraco.Core; + +namespace Umbraco.Web.Mvc +{ + /// + /// Abstract filtered controller factory used for all Umbraco controller factory implementations + /// + public abstract class UmbracoControllerFactory : IFilteredControllerFactory + { + private readonly OverridenDefaultControllerFactory _innerFactory = new OverridenDefaultControllerFactory(); + + public abstract bool CanHandle(RequestContext request); + + public virtual Type GetControllerType(RequestContext requestContext, string controllerName) + { + return _innerFactory.GetControllerType(requestContext, controllerName); + } + + /// + /// Creates the specified controller by using the specified request context. + /// + /// + /// The controller. + /// + /// The request context.The name of the controller. + public virtual IController CreateController(RequestContext requestContext, string controllerName) + { + Type controllerType = GetControllerType(requestContext, controllerName) ?? + _innerFactory.GetControllerType(requestContext, ControllerExtensions.GetControllerName(typeof(RenderMvcController))); + + return _innerFactory.GetControllerInstance(requestContext, controllerType); + } + + /// + /// Gets the controller's session behavior. + /// + /// + /// The controller's session behavior. + /// + /// The request context.The name of the controller whose session behavior you want to get. + public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) + { + return ((IControllerFactory)_innerFactory).GetControllerSessionBehavior(requestContext, controllerName); + } + + /// + /// Releases the specified controller. + /// + /// The controller. + public void ReleaseController(IController controller) + { + _innerFactory.ReleaseController(controller); + } + + /// + /// By default, only exposes which throws an exception + /// if the controller is not found. Since we want to try creating a controller, and then fall back to if one isn't found, + /// this nested class changes the visibility of 's internal methods in order to not have to rely on a try-catch. + /// + /// + internal class OverridenDefaultControllerFactory : DefaultControllerFactory + { + public new IController GetControllerInstance(RequestContext requestContext, Type controllerType) + { + return base.GetControllerInstance(requestContext, controllerType); + } + + public new Type GetControllerType(RequestContext requestContext, string controllerName) + { + if (controllerName.IsNullOrWhiteSpace()) + { + return null; + } + return base.GetControllerType(requestContext, controllerName); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ef55c9055f..22238d67e7 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -300,9 +300,11 @@ + + diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 9e130e8fe9..6e68259fb2 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -153,7 +153,9 @@ namespace Umbraco.Web umbracoPath + "/Surface/" + meta.ControllerName + "/{action}/{id}",//url to match new { controller = meta.ControllerName, action = "Index", id = UrlParameter.Optional }, new[] { meta.ControllerNamespace }); //only match this namespace - route.DataTokens.Add("umbraco", "surface"); //ensure the umbraco token is set + route.DataTokens.Add("umbraco", "surface"); //ensure the umbraco token is set + //make it use our custom/special SurfaceMvcHandler + route.RouteHandler = new SurfaceRouteHandler(); } //need to get the plugin controllers that are unique to each area (group by)