From 8e7ed865f07dfeee939fb144f5ba4d1808f1328c Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 27 Jun 2014 13:34:15 +1000 Subject: [PATCH] Fixes: U4-5151 Backoffice controllers collide on name even if different namespace --- src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../WebApi/NamespaceHttpControllerSelector.cs | 79 +++++++++++++++++++ src/Umbraco.Web/WebBootManager.cs | 4 + 3 files changed, 84 insertions(+) create mode 100644 src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 74af8127d7..e89fa232da 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -768,6 +768,7 @@ + diff --git a/src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs b/src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs new file mode 100644 index 0000000000..7151dca828 --- /dev/null +++ b/src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Web.Http; +using System.Web.Http.Controllers; +using System.Web.Http.Dispatcher; + +namespace Umbraco.Web.WebApi +{ + public class NamespaceHttpControllerSelector : DefaultHttpControllerSelector + { + private const string ControllerKey = "controller"; + private readonly HttpConfiguration _configuration; + private readonly Lazy> _duplicateControllerTypes; + + public NamespaceHttpControllerSelector(HttpConfiguration configuration) : base(configuration) + { + _configuration = configuration; + _duplicateControllerTypes = new Lazy>(GetDuplicateControllerTypes); + } + + public override HttpControllerDescriptor SelectController(HttpRequestMessage request) + { + var routeData = request.GetRouteData(); + if (routeData == null || routeData.Route == null || routeData.Route.DataTokens["Namespaces"] == null) + return base.SelectController(request); + + // Look up controller in route data + object controllerName; + routeData.Values.TryGetValue(ControllerKey, out controllerName); + var controllerNameAsString = controllerName as string; + if (controllerNameAsString == null) + return base.SelectController(request); + + //get the currently cached default controllers - this will not contain duplicate controllers found so if + // this controller is found in the underlying cache we don't need to do anything + var map = base.GetControllerMapping(); + if (map.ContainsKey(controllerNameAsString)) + return base.SelectController(request); + + //the cache does not contain this controller because it's most likely a duplicate, + // so we need to sort this out ourselves and we can only do that if the namespace token + // is formatted correctly. + var namespaces = routeData.Route.DataTokens["Namespaces"] as IEnumerable; + if (namespaces == null) + return base.SelectController(request); + + //see if this is in our cache + var found = _duplicateControllerTypes.Value + .Where(x => string.Equals(x.Name, controllerNameAsString + ControllerSuffix, StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(x => namespaces.Contains(x.Namespace)); + + if (found == null) + return base.SelectController(request); + + return new HttpControllerDescriptor(_configuration, controllerNameAsString, found); + } + + private IEnumerable GetDuplicateControllerTypes() + { + var assembliesResolver = _configuration.Services.GetAssembliesResolver(); + var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); + var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); + + //we have all controller types, so just store the ones with duplicate class names - we don't + // want to cache too much and the underlying selector caches everything else + + var duplicates = controllerTypes.GroupBy(x => x.Name) + .Where(x => x.Count() > 1) + .SelectMany(x => x); + + return duplicates; + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index c04d5acf69..88a44242dc 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Web; using System.Web.Configuration; using System.Web.Http; +using System.Web.Http.Dispatcher; using System.Web.Mvc; using System.Web.Routing; using ClientDependency.Core.Config; @@ -339,6 +340,9 @@ namespace Umbraco.Web new PublishedCache.XmlPublishedCache.PublishedContentCache(), new PublishedCache.XmlPublishedCache.PublishedMediaCache())); + GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), + new NamespaceHttpControllerSelector(GlobalConfiguration.Configuration)); + FilteredControllerFactoriesResolver.Current = new FilteredControllerFactoriesResolver( // add all known factories, devs can then modify this list on application // startup either by binding to events or in their own global.asax