diff --git a/src/Umbraco.Core/Configuration/Models/WebRoutingSettings.cs b/src/Umbraco.Core/Configuration/Models/WebRoutingSettings.cs
index 9f06046452..d94fdd8496 100644
--- a/src/Umbraco.Core/Configuration/Models/WebRoutingSettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/WebRoutingSettings.cs
@@ -10,6 +10,17 @@ namespace Umbraco.Core.Configuration.Models
///
public class WebRoutingSettings
{
+ ///
+ /// Gets or sets a value indicating whether to check if any routed endpoints match a front-end request before
+ /// the Umbraco dynamic router tries to map the request to an Umbraco content item.
+ ///
+ ///
+ /// This should not be necessary if the Umbraco catch-all/dynamic route is registered last like it's supposed to be. In that case
+ /// ASP.NET Core will automatically handle this in all cases. This is more of a backward compatible option since this is what v7/v8 used
+ /// to do.
+ ///
+ public bool TryMatchingEndpointsForAllPages { get; set; } = false;
+
///
/// Gets or sets a value indicating whether IIS custom errors should be skipped.
///
diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
index 6fa3328014..e670930691 100644
--- a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
+++ b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs
@@ -112,7 +112,6 @@ namespace Umbraco.Core.Routing
}
// check for special front-end paths
- // TODO: These should be constants - will need to update when we do front-end routing
if (urlPath.InvariantStartsWith(_surfaceMvcPath)
|| urlPath.InvariantStartsWith(_apiMvcPath))
{
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/RoutableDocumentFilterTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/RoutableDocumentFilterTests.cs
index b25da2351b..1c9cbc9c26 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/RoutableDocumentFilterTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/RoutableDocumentFilterTests.cs
@@ -18,6 +18,8 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
{
private IOptions GetGlobalSettings() => Options.Create(new GlobalSettings());
+ private IOptions GetWebRoutingSettings() => Options.Create(new WebRoutingSettings());
+
private IHostingEnvironment GetHostingEnvironment()
{
var hostingEnv = new Mock();
@@ -35,6 +37,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
{
var routableDocFilter = new RoutableDocumentFilter(
GetGlobalSettings(),
+ GetWebRoutingSettings(),
GetHostingEnvironment(),
new DefaultEndpointDataSource());
@@ -44,14 +47,14 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
[TestCase("/base/somebasehandler")]
[TestCase("/")]
- [TestCase("/home.aspx")]
+ [TestCase("/home")]
[TestCase("/umbraco-test")]
[TestCase("/install-test")]
- [TestCase("/install.aspx")]
public void Is_Not_Reserved_Path_Or_Url(string url)
{
var routableDocFilter = new RoutableDocumentFilter(
GetGlobalSettings(),
+ GetWebRoutingSettings(),
GetHostingEnvironment(),
new DefaultEndpointDataSource());
@@ -73,6 +76,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
public void Is_Reserved_By_Route(string url, bool isReserved)
{
var globalSettings = new GlobalSettings { ReservedPaths = string.Empty, ReservedUrls = string.Empty };
+ var routingSettings = new WebRoutingSettings { TryMatchingEndpointsForAllPages = true };
RouteEndpoint endpoint1 = CreateEndpoint(
"Umbraco/RenderMvc/{action?}/{id?}",
@@ -88,6 +92,7 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
var routableDocFilter = new RoutableDocumentFilter(
Options.Create(globalSettings),
+ Options.Create(routingSettings),
GetHostingEnvironment(),
endpointDataSource);
@@ -105,16 +110,18 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Web.Common.Routing
public void Is_Reserved_By_Default_Back_Office_Route(string url, bool isReserved)
{
var globalSettings = new GlobalSettings { ReservedPaths = string.Empty, ReservedUrls = string.Empty };
+ var routingSettings = new WebRoutingSettings { TryMatchingEndpointsForAllPages = true };
RouteEndpoint endpoint1 = CreateEndpoint(
- "umbraco/{action}/{id?}",
- new { controller = "BackOffice", action = "Default" },
+ "umbraco/{action?}/{id?}",
+ new { controller = "BackOffice" },
0);
var endpointDataSource = new DefaultEndpointDataSource(endpoint1);
var routableDocFilter = new RoutableDocumentFilter(
Options.Create(globalSettings),
+ Options.Create(routingSettings),
GetHostingEnvironment(),
endpointDataSource);
diff --git a/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs b/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
index 8f61918121..dee90bbfba 100644
--- a/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
+++ b/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
@@ -24,6 +24,7 @@ namespace Umbraco.Web.Common.Routing
{
private readonly ConcurrentDictionary _routeChecks = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
private readonly GlobalSettings _globalSettings;
+ private readonly WebRoutingSettings _routingSettings;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly EndpointDataSource _endpointDataSource;
private readonly object _routeLocker = new object();
@@ -34,9 +35,10 @@ namespace Umbraco.Web.Common.Routing
///
/// Initializes a new instance of the class.
///
- public RoutableDocumentFilter(IOptions globalSettings, IHostingEnvironment hostingEnvironment, EndpointDataSource endpointDataSource)
+ public RoutableDocumentFilter(IOptions globalSettings, IOptions routingSettings, IHostingEnvironment hostingEnvironment, EndpointDataSource endpointDataSource)
{
_globalSettings = globalSettings.Value;
+ _routingSettings = routingSettings.Value;
_hostingEnvironment = hostingEnvironment;
_endpointDataSource = endpointDataSource;
_endpointDataSource.GetChangeToken().RegisterChangeCallback(EndpointsChanged, null);
@@ -67,20 +69,17 @@ namespace Umbraco.Web.Common.Routing
// a document request should be
// /foo/bar/nil
// /foo/bar/nil/
- // /foo/bar/nil.aspx
// where /foo is not a reserved path
- // TODO: Remove aspx checks
-
- // if the path contains an extension that is not .aspx
+ // if the path contains an extension
// then it cannot be a document request
var extension = Path.GetExtension(absPath);
- if (maybeDoc && extension.IsNullOrWhiteSpace() == false && !extension.InvariantEquals(".aspx"))
+ if (maybeDoc && !extension.IsNullOrWhiteSpace())
{
maybeDoc = false;
}
- // at that point, either we have no extension, or it is .aspx
+ // at that point we have no extension
// if the path is reserved then it cannot be a document request
if (maybeDoc && IsReservedPathOrUrl(absPath))
@@ -143,16 +142,9 @@ namespace Umbraco.Web.Common.Routing
return true;
}
- // TODO: We have a problem here:
- // For every page that is rendered we are storing the URL and if it's routable in _routeChecks which
- // is a small memory leak. Not sure how we work around this since routes are all dynamic and we don't want
- // to double route everything on each request. Maybe instead of a growing list it's a list with a max capacity?
- // BUT... then do we need to do this at all? So long as the catch all route is registered LAST shouldn't all other routes
- // just match before it anyways and then we don't need to check? The strange part is that the "/umbraco" route doesn't automatically
- // match so we need to investigate that first.
-
- // check if the current request matches a route, if so then it is reserved.
- var hasRoute = _routeChecks.GetOrAdd(absPath, x => MatchesEndpoint(absPath));
+ // If configured, check if the current request matches a route, if so then it is reserved,
+ // else if not configured (default) proceed as normal since we assume the request is for an Umbraco content item.
+ var hasRoute = _routingSettings.TryMatchingEndpointsForAllPages && _routeChecks.GetOrAdd(absPath, x => MatchesEndpoint(absPath));
if (hasRoute)
{
return true;