diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs
index a319eb5a9e..5d5fb4646b 100644
--- a/src/Umbraco.Core/Constants-Web.cs
+++ b/src/Umbraco.Core/Constants-Web.cs
@@ -54,7 +54,7 @@
{
public const string InstallArea = "UmbracoInstall";
- public const string BackOfficeArea = "UmbracoBackOffice";
+ public const string BackOfficeArea = "UmbracoApi";
}
}
}
diff --git a/src/Umbraco.Core/Routing/UriUtility.cs b/src/Umbraco.Core/Routing/UriUtility.cs
index 0c68580204..ad785c111f 100644
--- a/src/Umbraco.Core/Routing/UriUtility.cs
+++ b/src/Umbraco.Core/Routing/UriUtility.cs
@@ -41,6 +41,7 @@ namespace Umbraco.Web
// adds the virtual directory if any
// see also VirtualPathUtility.ToAbsolute
+ // TODO: Does this do anything differently than IHostingEnvironment.ToAbsolute? Seems it does less, maybe should be removed?
public string ToAbsolute(string url)
{
//return ResolveUrl(url);
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj
index c5732870f3..67d822fb03 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj
@@ -20,6 +20,7 @@
+
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs
new file mode 100644
index 0000000000..a3a992bc22
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs
@@ -0,0 +1,108 @@
+using Microsoft.AspNetCore.Routing;
+using Moq;
+using NUnit.Framework;
+using System.Linq;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Hosting;
+using Umbraco.Extensions;
+using Umbraco.Web.BackOffice.Controllers;
+using Umbraco.Web.BackOffice.Routing;
+using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Controllers;
+using Umbraco.Web.WebApi;
+using Constants = Umbraco.Core.Constants;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
+{
+
+ [TestFixture]
+ public class BackOfficeAreaRoutesTests
+ {
+ [TestCase(RuntimeLevel.Install)]
+ [TestCase(RuntimeLevel.BootFailed)]
+ [TestCase(RuntimeLevel.Unknown)]
+ [TestCase(RuntimeLevel.Boot)]
+ public void RuntimeState_No_Routes(RuntimeLevel level)
+ {
+ var routes = GetBackOfficeAreaRoutes(level);
+ var endpoints = new TestRouteBuilder();
+ routes.CreateRoutes(endpoints);
+
+ Assert.AreEqual(0, endpoints.DataSources.Count);
+ }
+
+ [Test]
+ public void RuntimeState_Upgrade()
+ {
+ var routes = GetBackOfficeAreaRoutes(RuntimeLevel.Upgrade);
+ var endpoints = new TestRouteBuilder();
+ routes.CreateRoutes(endpoints);
+
+ Assert.AreEqual(1, endpoints.DataSources.Count);
+ var route = endpoints.DataSources.First();
+ Assert.AreEqual(2, route.Endpoints.Count);
+ AssertMinimalBackOfficeRoutes(route);
+ }
+
+ [Test]
+ public void RuntimeState_Run()
+ {
+ var routes = GetBackOfficeAreaRoutes(RuntimeLevel.Run);
+ var endpoints = new TestRouteBuilder();
+ routes.CreateRoutes(endpoints);
+
+ Assert.AreEqual(1, endpoints.DataSources.Count);
+ var route = endpoints.DataSources.First();
+ Assert.AreEqual(4, route.Endpoints.Count);
+ AssertMinimalBackOfficeRoutes(route);
+
+ var endpoint3 = (RouteEndpoint)route.Endpoints[2];
+ var previewControllerName = ControllerExtensions.GetControllerName();
+ Assert.AreEqual($"umbraco/{previewControllerName.ToLowerInvariant()}/{{action}}/{{id?}}", endpoint3.RoutePattern.RawText);
+ Assert.AreEqual(Constants.Web.Mvc.BackOfficeArea, endpoint3.RoutePattern.Defaults["area"]);
+ Assert.AreEqual("Index", endpoint3.RoutePattern.Defaults["action"]);
+ Assert.AreEqual(previewControllerName, endpoint3.RoutePattern.Defaults["controller"]);
+
+ var endpoint4 = (RouteEndpoint)route.Endpoints[3];
+ var apiControllerName = ControllerExtensions.GetControllerName();
+ Assert.AreEqual($"umbraco/backoffice/api/{apiControllerName.ToLowerInvariant()}/{{action}}/{{id?}}", endpoint4.RoutePattern.RawText);
+ Assert.IsFalse(endpoint4.RoutePattern.Defaults.ContainsKey("area"));
+ Assert.IsFalse(endpoint4.RoutePattern.Defaults.ContainsKey("action"));
+ Assert.AreEqual(apiControllerName, endpoint4.RoutePattern.Defaults["controller"]);
+ }
+
+ private void AssertMinimalBackOfficeRoutes(EndpointDataSource route)
+ {
+ var endpoint1 = (RouteEndpoint)route.Endpoints[0];
+ Assert.AreEqual($"umbraco/{{action}}/{{id?}}", endpoint1.RoutePattern.RawText);
+ Assert.AreEqual(Constants.Web.Mvc.BackOfficeArea, endpoint1.RoutePattern.Defaults["area"]);
+ Assert.AreEqual("Default", endpoint1.RoutePattern.Defaults["action"]);
+ Assert.AreEqual(ControllerExtensions.GetControllerName(), endpoint1.RoutePattern.Defaults["controller"]);
+
+ var endpoint2 = (RouteEndpoint)route.Endpoints[1];
+ var controllerName = ControllerExtensions.GetControllerName();
+ Assert.AreEqual($"umbraco/backoffice/{Constants.Web.Mvc.BackOfficeArea.ToLowerInvariant()}/{controllerName.ToLowerInvariant()}/{{action}}/{{id?}}", endpoint2.RoutePattern.RawText);
+ Assert.AreEqual(Constants.Web.Mvc.BackOfficeArea, endpoint2.RoutePattern.Defaults["area"]);
+ Assert.IsFalse(endpoint2.RoutePattern.Defaults.ContainsKey("action"));
+ Assert.AreEqual(controllerName, endpoint2.RoutePattern.Defaults["controller"]);
+ }
+
+ private BackOfficeAreaRoutes GetBackOfficeAreaRoutes(RuntimeLevel level)
+ {
+ var routes = new BackOfficeAreaRoutes(
+ Mock.Of(x => x.UmbracoPath == "~/umbraco"),
+ Mock.Of(x => x.ToAbsolute(It.IsAny()) == "/umbraco" && x.ApplicationVirtualPath == string.Empty),
+ Mock.Of(x => x.Level == level),
+ new UmbracoApiControllerTypeCollection(new[] { typeof(Testing1Controller) }));
+
+ return routes;
+ }
+
+ [IsBackOffice]
+ private class Testing1Controller : UmbracoApiController
+ {
+
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/EndpointRouteBuilderExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/EndpointRouteBuilderExtensionsTests.cs
index 2c6b17e08b..4c4d282813 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/EndpointRouteBuilderExtensionsTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/EndpointRouteBuilderExtensionsTests.cs
@@ -1,21 +1,18 @@
-using Microsoft.AspNetCore.Builder;
-using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
-using Microsoft.Extensions.DependencyInjection;
-using Moq;
-using NUglify.Helpers;
using NUnit.Framework;
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Umbraco.Core;
using Umbraco.Extensions;
using Umbraco.Web.Common.Routing;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
{
+
[TestFixture]
public class EndpointRouteBuilderExtensionsTests
{
@@ -110,41 +107,6 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
Assert.AreEqual(controllerName, endpoint.RoutePattern.Defaults["controller"]);
}
- private class TestRouteBuilder : IEndpointRouteBuilder
- {
- private readonly ServiceProvider _serviceProvider;
-
- public TestRouteBuilder()
- {
- var services = new ServiceCollection();
- services.AddLogging();
- services.AddMvc();
- _serviceProvider = services.BuildServiceProvider();
- }
-
- public ICollection DataSources { get; } = new List();
-
- public IServiceProvider ServiceProvider => _serviceProvider;
-
- public IApplicationBuilder CreateApplicationBuilder()
- {
- return Mock.Of();
- }
- }
-
- private class TestServiceProvider : IServiceProvider
- {
- public object GetService(Type serviceType)
- {
- if (serviceType.Name == "MvcMarkerService")
- {
- // it's internal but we can force make it
- return Activator.CreateInstance(serviceType);
- }
- return null;
- }
- }
-
private class Testing1Controller : ControllerBase
{
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/InstallAreaRoutesTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/InstallAreaRoutesTests.cs
new file mode 100644
index 0000000000..238554e8b7
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/InstallAreaRoutesTests.cs
@@ -0,0 +1,82 @@
+using Microsoft.AspNetCore.Routing;
+using Moq;
+using NUnit.Framework;
+using System.Linq;
+using Umbraco.Core;
+using Umbraco.Core.Hosting;
+using Umbraco.Extensions;
+using Umbraco.Web.Common.Install;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
+{
+ [TestFixture]
+ public class InstallAreaRoutesTests
+ {
+ [TestCase(RuntimeLevel.BootFailed)]
+ [TestCase(RuntimeLevel.Unknown)]
+ [TestCase(RuntimeLevel.Boot)]
+ public void RuntimeState_No_Routes(RuntimeLevel level)
+ {
+ var routes = GetInstallAreaRoutes(level);
+ var endpoints = new TestRouteBuilder();
+ routes.CreateRoutes(endpoints);
+
+ Assert.AreEqual(0, endpoints.DataSources.Count);
+ }
+
+ [TestCase(RuntimeLevel.Install)]
+ [TestCase(RuntimeLevel.Upgrade)]
+ public void RuntimeState_Install(RuntimeLevel level)
+ {
+ var routes = GetInstallAreaRoutes(level);
+ var endpoints = new TestRouteBuilder();
+ routes.CreateRoutes(endpoints);
+
+ Assert.AreEqual(2, endpoints.DataSources.Count);
+ var route = endpoints.DataSources.First();
+ Assert.AreEqual(2, route.Endpoints.Count);
+
+ var endpoint1 = (RouteEndpoint)route.Endpoints[0];
+ Assert.AreEqual($"install/api/{{action}}/{{id?}}", endpoint1.RoutePattern.RawText);
+ Assert.AreEqual(Constants.Web.Mvc.InstallArea, endpoint1.RoutePattern.Defaults["area"]);
+ Assert.AreEqual("Index", endpoint1.RoutePattern.Defaults["action"]);
+ Assert.AreEqual(ControllerExtensions.GetControllerName(), endpoint1.RoutePattern.Defaults["controller"]);
+
+ var endpoint2 = (RouteEndpoint)route.Endpoints[1];
+ Assert.AreEqual($"install/{{action}}/{{id?}}", endpoint2.RoutePattern.RawText);
+ Assert.AreEqual(Constants.Web.Mvc.InstallArea, endpoint2.RoutePattern.Defaults["area"]);
+ Assert.AreEqual("Index", endpoint2.RoutePattern.Defaults["action"]);
+ Assert.AreEqual(ControllerExtensions.GetControllerName(), endpoint2.RoutePattern.Defaults["controller"]);
+
+ var fallbackRoute = endpoints.DataSources.Last();
+ Assert.AreEqual(1, fallbackRoute.Endpoints.Count);
+
+ Assert.AreEqual("Fallback {*path:nonfile}", fallbackRoute.Endpoints[0].ToString());
+ }
+
+ [Test]
+ public void RuntimeState_Run()
+ {
+ var routes = GetInstallAreaRoutes(RuntimeLevel.Run);
+ var endpoints = new TestRouteBuilder();
+ routes.CreateRoutes(endpoints);
+
+ Assert.AreEqual(1, endpoints.DataSources.Count);
+ var route = endpoints.DataSources.First();
+ Assert.AreEqual(1, route.Endpoints.Count);
+
+ Assert.AreEqual("install/{controller?}/{action?} HTTP: GET", route.Endpoints[0].ToString());
+
+ }
+
+ private InstallAreaRoutes GetInstallAreaRoutes(RuntimeLevel level)
+ {
+ var routes = new InstallAreaRoutes(
+ Mock.Of(x => x.Level == level),
+ Mock.Of(x => x.ToAbsolute(It.IsAny()) == "/install" && x.ApplicationVirtualPath == string.Empty),
+ Mock.Of());
+
+ return routes;
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/TestRouteBuilder.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/TestRouteBuilder.cs
new file mode 100644
index 0000000000..6d28e8a71e
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/TestRouteBuilder.cs
@@ -0,0 +1,31 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
+using Moq;
+using System;
+using System.Collections.Generic;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
+{
+ public class TestRouteBuilder : IEndpointRouteBuilder
+ {
+ private readonly ServiceProvider _serviceProvider;
+
+ public TestRouteBuilder()
+ {
+ var services = new ServiceCollection();
+ services.AddLogging();
+ services.AddMvc();
+ _serviceProvider = services.BuildServiceProvider();
+ }
+
+ public ICollection DataSources { get; } = new List();
+
+ public IServiceProvider ServiceProvider => _serviceProvider;
+
+ public IApplicationBuilder CreateApplicationBuilder()
+ {
+ return Mock.Of();
+ }
+ }
+}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
index 2f87ee8b6f..67a2086b0d 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
@@ -5,7 +5,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Controllers
{
- [Area(Constants.Web.Mvc.BackOfficeArea)] // TODO: Maybe this could be applied with our Application Model conventions
+ [PluginController(Constants.Web.Mvc.BackOfficeArea)] // TODO: Maybe this could be applied with our Application Model conventions
//[ValidationFilter] // TODO: I don't actually think this is required with our custom Application Model conventions applied
[TypeFilter(typeof(AngularJsonOnlyConfigurationAttribute))] // TODO: This could be applied with our Application Model conventions
[IsBackOffice] // TODO: This could be applied with our Application Model conventions
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
index 5d5fd120de..c99498bf57 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
@@ -7,11 +7,12 @@ using Umbraco.Net;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.ActionResults;
using Umbraco.Web.WebAssets;
+using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Controllers
{
- [Area(Umbraco.Core.Constants.Web.Mvc.BackOfficeArea)]
+ [Area(Constants.Web.Mvc.BackOfficeArea)]
public class BackOfficeController : Controller
{
private readonly IRuntimeMinifier _runtimeMinifier;
diff --git a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs
index bd2aeac475..3472302a24 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs
@@ -22,7 +22,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.BackOffice.Controllers
{
[DisableBrowserCache]
- [Area(Umbraco.Core.Constants.Web.Mvc.BackOfficeArea)]
+ [Area(Constants.Web.Mvc.BackOfficeArea)]
public class PreviewController : Controller
{
private readonly UmbracoFeatures _features;
diff --git a/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs b/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs
index 457a5dc214..42daf6db9b 100644
--- a/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs
+++ b/src/Umbraco.Web.BackOffice/Routing/BackOfficeAreaRoutes.cs
@@ -47,7 +47,7 @@ namespace Umbraco.Web.BackOffice.Routing
case RuntimeLevel.Run:
MapMinimalBackOffice(endpoints);
- endpoints.MapUmbracoRoute(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeArea, "preview");
+ endpoints.MapUmbracoRoute(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeArea, null);
AutoRouteBackOfficeControllers(endpoints);
break;
@@ -67,6 +67,7 @@ namespace Umbraco.Web.BackOffice.Routing
endpoints.MapUmbracoRoute(_umbracoPathSegment, Constants.Web.Mvc.BackOfficeArea,
string.Empty,
"Default",
+ includeControllerNameInRoute: false,
constraints:
// Limit the action/id to only allow characters - this is so this route doesn't hog all other
// routes like: /umbraco/channels/word.aspx, etc...
diff --git a/src/Umbraco.Web.Common/Install/InstallAreaRoutes.cs b/src/Umbraco.Web.Common/Install/InstallAreaRoutes.cs
index b024743e72..6f1f378809 100644
--- a/src/Umbraco.Web.Common/Install/InstallAreaRoutes.cs
+++ b/src/Umbraco.Web.Common/Install/InstallAreaRoutes.cs
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Routing;
using System;
using System.Threading.Tasks;
using Umbraco.Core;
+using Umbraco.Core.Hosting;
using Umbraco.Core.Logging;
using Umbraco.Extensions;
using Umbraco.Web.Common.Routing;
@@ -14,19 +15,19 @@ namespace Umbraco.Web.Common.Install
public class InstallAreaRoutes : IAreaRoutes
{
private readonly IRuntimeState _runtime;
- private readonly UriUtility _uriUtility;
+ private readonly IHostingEnvironment _hostingEnvironment;
private readonly LinkGenerator _linkGenerator;
- public InstallAreaRoutes(IRuntimeState runtime, UriUtility uriUtility, LinkGenerator linkGenerator)
+ public InstallAreaRoutes(IRuntimeState runtime, IHostingEnvironment hostingEnvironment, LinkGenerator linkGenerator)
{
_runtime = runtime;
- _uriUtility = uriUtility;
+ _hostingEnvironment = hostingEnvironment;
_linkGenerator = linkGenerator;
}
public void CreateRoutes(IEndpointRouteBuilder endpoints)
{
- var installPathSegment = _uriUtility.ToAbsolute(Core.Constants.SystemDirectories.Install);
+ var installPathSegment = _hostingEnvironment.ToAbsolute(Core.Constants.SystemDirectories.Install).TrimStart('/');
switch (_runtime.Level)
{