diff --git a/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs
index 46571f5d65..500bd65f82 100644
--- a/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs
+++ b/src/Umbraco.Core/Routing/ContentFinderByIdPath.cs
@@ -49,7 +49,7 @@ namespace Umbraco.Web.Routing
}
IPublishedContent node = null;
- var path = frequest.Uri.GetAbsolutePathDecoded();
+ var path = frequest.AbsolutePathDecoded;
var nodeId = -1;
diff --git a/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs
index 38f04d1ddb..e3c5b28a2a 100644
--- a/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs
+++ b/src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs
@@ -51,8 +51,8 @@ namespace Umbraco.Web.Routing
}
var route = frequest.Domain != null
- ? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded())
- : frequest.Uri.GetAbsolutePathDecoded();
+ ? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.AbsolutePathDecoded)
+ : frequest.AbsolutePathDecoded;
IRedirectUrl redirectUrl = _redirectUrlService.GetMostRecentRedirectUrl(route, frequest.Culture);
diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrl.cs b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs
index 27893cd3de..c20cf9fd85 100644
--- a/src/Umbraco.Core/Routing/ContentFinderByUrl.cs
+++ b/src/Umbraco.Core/Routing/ContentFinderByUrl.cs
@@ -44,11 +44,11 @@ namespace Umbraco.Web.Routing
string route;
if (frequest.Domain != null)
{
- route = frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.Uri.GetAbsolutePathDecoded());
+ route = frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.AbsolutePathDecoded);
}
else
{
- route = frequest.Uri.GetAbsolutePathDecoded();
+ route = frequest.AbsolutePathDecoded;
}
IPublishedContent node = FindContent(frequest, route);
diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs
index 770fdf4003..4745ea8cd3 100644
--- a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs
+++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs
@@ -60,7 +60,7 @@ namespace Umbraco.Web.Routing
umbCtx.Content,
frequest.Domain != null ? frequest.Domain.ContentId : 0,
frequest.Culture,
- frequest.Uri.GetAbsolutePathDecoded());
+ frequest.AbsolutePathDecoded);
if (node != null)
{
diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs
index c6bd4f383d..2e69446d68 100644
--- a/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs
+++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAndTemplate.cs
@@ -49,7 +49,7 @@ namespace Umbraco.Web.Routing
/// If successful, also assigns the template.
public override bool TryFindContent(IPublishedRequestBuilder frequest)
{
- var path = frequest.Uri.GetAbsolutePathDecoded();
+ var path = frequest.AbsolutePathDecoded;
if (frequest.Domain != null)
{
diff --git a/src/Umbraco.Core/Routing/DomainUtilities.cs b/src/Umbraco.Core/Routing/DomainUtilities.cs
index fa5d84836d..0d14b26396 100644
--- a/src/Umbraco.Core/Routing/DomainUtilities.cs
+++ b/src/Umbraco.Core/Routing/DomainUtilities.cs
@@ -364,9 +364,7 @@ namespace Umbraco.Web.Routing
/// The path part relative to the uri of the domain.
/// Eg the relative part of /foo/bar/nil to domain example.com/foo is /bar/nil.
public static string PathRelativeToDomain(Uri domainUri, string path)
- {
- return path.Substring(domainUri.GetAbsolutePathDecoded().Length).EnsureStartsWith('/');
- }
+ => path.Substring(domainUri.GetAbsolutePathDecoded().Length).EnsureStartsWith('/');
#endregion
}
diff --git a/src/Umbraco.Core/Routing/IPublishedRequest.cs b/src/Umbraco.Core/Routing/IPublishedRequest.cs
index fedfd69dc3..58523d12e4 100644
--- a/src/Umbraco.Core/Routing/IPublishedRequest.cs
+++ b/src/Umbraco.Core/Routing/IPublishedRequest.cs
@@ -17,6 +17,11 @@ namespace Umbraco.Web.Routing
/// The cleaned up Uri has no virtual directory, no trailing slash, no .aspx extension, etc.
Uri Uri { get; }
+ ///
+ /// Gets the URI decoded absolute path of the
+ ///
+ string AbsolutePathDecoded { get; }
+
///
/// Gets a value indicating the requested content.
///
diff --git a/src/Umbraco.Core/Routing/IPublishedRequestBuilder.cs b/src/Umbraco.Core/Routing/IPublishedRequestBuilder.cs
index ced443a89c..bd5b5625a3 100644
--- a/src/Umbraco.Core/Routing/IPublishedRequestBuilder.cs
+++ b/src/Umbraco.Core/Routing/IPublishedRequestBuilder.cs
@@ -18,6 +18,11 @@ namespace Umbraco.Web.Routing
/// The cleaned up Uri has no virtual directory, no trailing slash, no .aspx extension, etc.
Uri Uri { get; }
+ ///
+ /// Gets the URI decoded absolute path of the
+ ///
+ string AbsolutePathDecoded { get; }
+
///
/// Gets the assigned (if any)
///
diff --git a/src/Umbraco.Core/Routing/PublishedRequest.cs b/src/Umbraco.Core/Routing/PublishedRequest.cs
index e42211da49..7a3d44149d 100644
--- a/src/Umbraco.Core/Routing/PublishedRequest.cs
+++ b/src/Umbraco.Core/Routing/PublishedRequest.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
-using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
@@ -13,9 +11,10 @@ namespace Umbraco.Web.Routing
///
/// Initializes a new instance of the class.
///
- public PublishedRequest(Uri uri, IPublishedContent publishedContent, bool isInternalRedirect, ITemplate template, DomainAndUri domain, string culture, string redirectUrl, int? responseStatusCode, IReadOnlyList cacheExtensions, IReadOnlyDictionary headers, bool setNoCacheHeader, bool ignorePublishedContentCollisions)
+ public PublishedRequest(Uri uri, string absolutePathDecoded, IPublishedContent publishedContent, bool isInternalRedirect, ITemplate template, DomainAndUri domain, string culture, string redirectUrl, int? responseStatusCode, IReadOnlyList cacheExtensions, IReadOnlyDictionary headers, bool setNoCacheHeader, bool ignorePublishedContentCollisions)
{
Uri = uri ?? throw new ArgumentNullException(nameof(uri));
+ AbsolutePathDecoded = absolutePathDecoded ?? throw new ArgumentNullException(nameof(absolutePathDecoded));
PublishedContent = publishedContent;
IsInternalRedirect = isInternalRedirect;
Template = template;
@@ -32,6 +31,9 @@ namespace Umbraco.Web.Routing
///
public Uri Uri { get; }
+ ///
+ public string AbsolutePathDecoded { get; }
+
///
public bool IgnorePublishedContentCollisions { get; }
diff --git a/src/Umbraco.Core/Routing/PublishedRequestBuilder.cs b/src/Umbraco.Core/Routing/PublishedRequestBuilder.cs
index faa793c7ff..606031564b 100644
--- a/src/Umbraco.Core/Routing/PublishedRequestBuilder.cs
+++ b/src/Umbraco.Core/Routing/PublishedRequestBuilder.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
+using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
@@ -26,12 +27,16 @@ namespace Umbraco.Web.Routing
public PublishedRequestBuilder(Uri uri, IFileService fileService)
{
Uri = uri;
+ AbsolutePathDecoded = uri.GetAbsolutePathDecoded();
_fileService = fileService;
}
///
public Uri Uri { get; }
+ ///
+ public string AbsolutePathDecoded { get; }
+
///
public DomainAndUri Domain { get; private set; }
@@ -62,6 +67,7 @@ namespace Umbraco.Web.Routing
///
public IPublishedRequest Build() => new PublishedRequest(
Uri,
+ AbsolutePathDecoded,
PublishedContent,
IsInternalRedirect,
Template,
diff --git a/src/Umbraco.Core/Routing/UriUtility.cs b/src/Umbraco.Core/Routing/UriUtility.cs
index 8de78dfbf5..43a36db101 100644
--- a/src/Umbraco.Core/Routing/UriUtility.cs
+++ b/src/Umbraco.Core/Routing/UriUtility.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Text;
using Umbraco.Core;
using Umbraco.Core.Configuration;
@@ -55,9 +55,15 @@ namespace Umbraco.Web
{
if (virtualPath.InvariantStartsWith(_appPathPrefix)
&& (virtualPath.Length == _appPathPrefix.Length || virtualPath[_appPathPrefix.Length] == '/'))
+ {
virtualPath = virtualPath.Substring(_appPathPrefix.Length);
+ }
+
if (virtualPath.Length == 0)
+ {
virtualPath = "/";
+ }
+
return virtualPath;
}
@@ -88,6 +94,9 @@ namespace Umbraco.Web
// ie no virtual directory, no .aspx, lowercase...
public Uri UriToUmbraco(Uri uri)
{
+ // TODO: This is critical code that executes on every request, we should
+ // look into if all of this is necessary? not really sure we need ToLower?
+
// note: no need to decode uri here because we're returning a uri
// so it will be re-encoded anyway
var path = uri.GetSafeAbsolutePath();
@@ -95,23 +104,11 @@ namespace Umbraco.Web
path = path.ToLower();
path = ToAppRelative(path); // strip vdir if any
- //we need to check if the path is /default.aspx because this will occur when using a
- //web server pre IIS 7 when requesting the root document
- //if this is the case we need to change it to '/'
- if (path.StartsWith("/default.aspx", StringComparison.InvariantCultureIgnoreCase))
- {
- string rempath = path.Substring("/default.aspx".Length, path.Length - "/default.aspx".Length);
- path = rempath.StartsWith("/") ? rempath : "/" + rempath;
- }
if (path != "/")
{
path = path.TrimEnd('/');
}
- //if any part of the path contains .aspx, replace it with nothing.
- //sometimes .aspx is not at the end since we might have /home/sub1.aspx/customtemplate
- path = path.Replace(".aspx", "");
-
return uri.Rewrite(path);
}
diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs
index 497159309a..26580fab84 100644
--- a/src/Umbraco.Core/UriExtensions.cs
+++ b/src/Umbraco.Core/UriExtensions.cs
@@ -61,10 +61,14 @@ namespace Umbraco.Core
public static string GetSafeAbsolutePath(this Uri uri)
{
if (uri.IsAbsoluteUri)
+ {
return uri.AbsolutePath;
+ }
// cannot get .AbsolutePath on relative uri (InvalidOperation)
var s = uri.OriginalString;
+
+ // TODO: Shouldn't this just use Uri.GetLeftPart?
var posq = s.IndexOf("?", StringComparison.Ordinal);
var posf = s.IndexOf("#", StringComparison.Ordinal);
var pos = posq > 0 ? posq : (posf > 0 ? posf : 0);
diff --git a/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs b/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs
index 5634fa4a93..bc9f9f3857 100644
--- a/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs
+++ b/src/Umbraco.Infrastructure/Routing/ContentFinderByConfigured404.cs
@@ -61,7 +61,7 @@ namespace Umbraco.Web.Routing
}
else
{
- var route = frequest.Uri.GetAbsolutePathDecoded();
+ var route = frequest.AbsolutePathDecoded;
var pos = route.LastIndexOf('/');
IPublishedContent node = null;
while (pos > 1)
diff --git a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj
index aa39070cc7..bdb703753d 100644
--- a/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj
+++ b/src/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
index c1d0fe3758..dbb3fa1137 100644
--- a/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
+++ b/src/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj
@@ -59,7 +59,7 @@
-
+
all
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UriUtilityTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UriUtilityTests.cs
index dee487621f..4789698fcd 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UriUtilityTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UriUtilityTests.cs
@@ -23,28 +23,12 @@ namespace Umbraco.Tests.UnitTests.Umbraco.Core.Routing
[TestCase("http://LocalHost/Home/Sub1", "http://localhost/home/sub1")]
[TestCase("http://LocalHost/Home/Sub1?x=y", "http://localhost/home/sub1?x=y")]
- // same with .aspx
- [TestCase("http://LocalHost/Home.aspx", "http://localhost/home")]
- [TestCase("http://LocalHost/Home.aspx?x=y", "http://localhost/home?x=y")]
- [TestCase("http://LocalHost/Home/Sub1.aspx", "http://localhost/home/sub1")]
- [TestCase("http://LocalHost/Home/Sub1.aspx?x=y", "http://localhost/home/sub1?x=y")]
-
// test that the trailing slash goes but not on hostname
[TestCase("http://LocalHost/", "http://localhost/")]
[TestCase("http://LocalHost/Home/", "http://localhost/home")]
[TestCase("http://LocalHost/Home/?x=y", "http://localhost/home?x=y")]
[TestCase("http://LocalHost/Home/Sub1/", "http://localhost/home/sub1")]
[TestCase("http://LocalHost/Home/Sub1/?x=y", "http://localhost/home/sub1?x=y")]
-
- // test that default.aspx goes, even with parameters
- [TestCase("http://LocalHost/deFault.aspx", "http://localhost/")]
- [TestCase("http://LocalHost/deFault.aspx?x=y", "http://localhost/?x=y")]
-
- // test with inner .aspx
- [TestCase("http://Localhost/Home/Sub1.aspx/Sub2", "http://localhost/home/sub1/sub2")]
- [TestCase("http://Localhost/Home/Sub1.aspx/Sub2?x=y", "http://localhost/home/sub1/sub2?x=y")]
- [TestCase("http://Localhost/Home.aspx/Sub1.aspx/Sub2?x=y", "http://localhost/home/sub1/sub2?x=y")]
- [TestCase("http://Localhost/deFault.aspx/Home.aspx/deFault.aspx/Sub1.aspx", "http://localhost/home/default/sub1")]
public void Uri_To_Umbraco(string sourceUrl, string expectedUrl)
{
// Arrange
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/HijackedRouteEvaluatorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/HijackedRouteEvaluatorTests.cs
new file mode 100644
index 0000000000..2d96476b30
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/HijackedRouteEvaluatorTests.cs
@@ -0,0 +1,104 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Abstractions;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.AspNetCore.Mvc.ViewEngines;
+using Microsoft.Extensions.FileProviders;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Primitives;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Extensions;
+using Umbraco.Web;
+using Umbraco.Web.Common.Controllers;
+using Umbraco.Web.Website.Routing;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
+{
+
+ [TestFixture]
+ public class HijackedRouteEvaluatorTests
+ {
+ private class TestActionDescriptorCollectionProvider : ActionDescriptorCollectionProvider
+ {
+ private readonly IEnumerable _actions;
+
+ public TestActionDescriptorCollectionProvider(IEnumerable actions) => _actions = actions;
+
+ public override ActionDescriptorCollection ActionDescriptors => new ActionDescriptorCollection(_actions.ToList(), 1);
+
+ public override IChangeToken GetChangeToken() => NullChangeToken.Singleton;
+ }
+
+ private IActionDescriptorCollectionProvider GetActionDescriptors() => new TestActionDescriptorCollectionProvider(
+ new ActionDescriptor[]
+ {
+ new ControllerActionDescriptor
+ {
+ ActionName = "Index",
+ ControllerName = ControllerExtensions.GetControllerName(),
+ ControllerTypeInfo = typeof(RenderController).GetTypeInfo()
+ },
+ new ControllerActionDescriptor
+ {
+ ActionName = "Index",
+ ControllerName = ControllerExtensions.GetControllerName(),
+ ControllerTypeInfo = typeof(Render1Controller).GetTypeInfo()
+ },
+ new ControllerActionDescriptor
+ {
+ ActionName = "Custom",
+ ControllerName = ControllerExtensions.GetControllerName(),
+ ControllerTypeInfo = typeof(Render1Controller).GetTypeInfo()
+ },
+ new ControllerActionDescriptor
+ {
+ ActionName = "Index",
+ ControllerName = ControllerExtensions.GetControllerName(),
+ ControllerTypeInfo = typeof(Render2Controller).GetTypeInfo()
+ }
+ });
+
+ private class Render1Controller : ControllerBase, IRenderController
+ {
+ public IActionResult Index => Content("hello world");
+
+ public IActionResult Custom => Content("hello world");
+ }
+
+ private class Render2Controller : RenderController
+ {
+ public Render2Controller(ILogger logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
+ : base(logger, compositeViewEngine, umbracoContextAccessor)
+ {
+ }
+ }
+
+ [TestCase("Index", "RenderNotFound", null, false)]
+ [TestCase("index", "Render", nameof(RenderController.Index), true)]
+ [TestCase("Index", "Render1", nameof(RenderController.Index), true)]
+ [TestCase("Index", "render2", nameof(Render2Controller.Index), true)]
+ [TestCase("NotFound", "Render", nameof(RenderController.Index), true)]
+ [TestCase("NotFound", "Render1", nameof(Render1Controller.Index), true)]
+ [TestCase("NotFound", "Render2", nameof(Render2Controller.Index), true)]
+ [TestCase("Custom", "Render1", nameof(Render1Controller.Custom), true)]
+ public void Matches_Controller(string action, string controller, string resultAction, bool matches)
+ {
+ var evaluator = new HijackedRouteEvaluator(
+ new NullLogger(),
+ GetActionDescriptors());
+
+ HijackedRouteResult result = evaluator.Evaluate(controller, action);
+ Assert.AreEqual(matches, result.Success);
+ if (matches)
+ {
+ Assert.IsTrue(result.ActionName.InvariantEquals(resultAction), "expected {0} does not match resulting action {1}", resultAction, result.ActionName);
+ Assert.IsTrue(result.ControllerName.InvariantEquals(controller), "expected {0} does not match resulting controller {1}", controller, result.ControllerName);
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs
new file mode 100644
index 0000000000..a531c77fe1
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.ViewEngines;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Options;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Configuration.Models;
+using Umbraco.Extensions;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Web;
+using Umbraco.Web.Common.Controllers;
+using Umbraco.Web.Common.Routing;
+using Umbraco.Web.PublishedCache;
+using Umbraco.Web.Routing;
+using Umbraco.Web.Website.Controllers;
+using Umbraco.Web.Website.Routing;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
+{
+ [TestFixture]
+ public class UmbracoRouteValueTransformerTests
+ {
+ private IOptions GetGlobalSettings() => Options.Create(new GlobalSettings());
+
+ private UmbracoRouteValueTransformer GetTransformerWithRunState(
+ IUmbracoContextAccessor ctx,
+ IRoutableDocumentFilter filter = null,
+ IPublishedRouter router = null,
+ IUmbracoRouteValuesFactory routeValuesFactory = null)
+ => GetTransformer(ctx, Mock.Of(x => x.Level == RuntimeLevel.Run), filter, router, routeValuesFactory);
+
+ private UmbracoRouteValueTransformer GetTransformer(
+ IUmbracoContextAccessor ctx,
+ IRuntimeState state,
+ IRoutableDocumentFilter filter = null,
+ IPublishedRouter router = null,
+ IUmbracoRouteValuesFactory routeValuesFactory = null)
+ {
+ var transformer = new UmbracoRouteValueTransformer(
+ new NullLogger(),
+ ctx,
+ router ?? Mock.Of(),
+ GetGlobalSettings(),
+ TestHelper.GetHostingEnvironment(),
+ state,
+ routeValuesFactory ?? Mock.Of(),
+ filter ?? Mock.Of(x => x.IsDocumentRequest(It.IsAny()) == true));
+
+ return transformer;
+ }
+
+ private IUmbracoContext GetUmbracoContext(bool hasContent)
+ {
+ IPublishedContentCache publishedContent = Mock.Of(x => x.HasContent() == hasContent);
+ var uri = new Uri("http://example.com");
+
+ IUmbracoContext umbracoContext = Mock.Of(x =>
+ x.Content == publishedContent
+ && x.OriginalRequestUrl == uri
+ && x.CleanedUmbracoUrl == uri);
+
+ return umbracoContext;
+ }
+
+ private UmbracoRouteValues GetRouteValues(IPublishedRequest request)
+ => new UmbracoRouteValues(
+ request,
+ ControllerExtensions.GetControllerName(),
+ typeof(TestController));
+
+ private IUmbracoRouteValuesFactory GetRouteValuesFactory(IPublishedRequest request)
+ => Mock.Of(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny()) == GetRouteValues(request));
+
+ private IPublishedRouter GetRouter(IPublishedRequest request)
+ => Mock.Of(x => x.RouteRequestAsync(It.IsAny(), It.IsAny()) == Task.FromResult(request));
+
+ [Test]
+ public async Task Noop_When_Runtime_Level_Not_Run()
+ {
+ UmbracoRouteValueTransformer transformer = GetTransformer(
+ Mock.Of(),
+ Mock.Of());
+
+ RouteValueDictionary result = await transformer.TransformAsync(new DefaultHttpContext(), new RouteValueDictionary());
+ Assert.AreEqual(0, result.Count);
+ }
+
+ [Test]
+ public async Task Noop_When_No_Umbraco_Context()
+ {
+ UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
+ Mock.Of());
+
+ RouteValueDictionary result = await transformer.TransformAsync(new DefaultHttpContext(), new RouteValueDictionary());
+ Assert.AreEqual(0, result.Count);
+ }
+
+ [Test]
+ public async Task Noop_When_Not_Document_Request()
+ {
+ UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
+ Mock.Of(x => x.UmbracoContext == Mock.Of()),
+ Mock.Of(x => x.IsDocumentRequest(It.IsAny()) == false));
+
+ RouteValueDictionary result = await transformer.TransformAsync(new DefaultHttpContext(), new RouteValueDictionary());
+ Assert.AreEqual(0, result.Count);
+ }
+
+ [Test]
+ public async Task NoContentController_Values_When_No_Content()
+ {
+ IUmbracoContext umbracoContext = GetUmbracoContext(false);
+
+ UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
+ Mock.Of(x => x.UmbracoContext == umbracoContext));
+
+ RouteValueDictionary result = await transformer.TransformAsync(new DefaultHttpContext(), new RouteValueDictionary());
+ Assert.AreEqual(2, result.Count);
+ Assert.AreEqual(ControllerExtensions.GetControllerName(), result["controller"]);
+ Assert.AreEqual(nameof(RenderNoContentController.Index), result["action"]);
+ }
+
+ [Test]
+ public async Task Assigns_PublishedRequest_To_UmbracoContext()
+ {
+ IUmbracoContext umbracoContext = GetUmbracoContext(true);
+ IPublishedRequest request = Mock.Of();
+
+ UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
+ Mock.Of(x => x.UmbracoContext == umbracoContext),
+ router: GetRouter(request),
+ routeValuesFactory: GetRouteValuesFactory(request));
+
+ RouteValueDictionary result = await transformer.TransformAsync(new DefaultHttpContext(), new RouteValueDictionary());
+ Assert.AreEqual(request, umbracoContext.PublishedRequest);
+ }
+
+ [Test]
+ public async Task Assigns_Values_To_RouteValueDictionary()
+ {
+ IUmbracoContext umbracoContext = GetUmbracoContext(true);
+ IPublishedRequest request = Mock.Of();
+ UmbracoRouteValues routeValues = GetRouteValues(request);
+
+ UmbracoRouteValueTransformer transformer = GetTransformerWithRunState(
+ Mock.Of(x => x.UmbracoContext == umbracoContext),
+ router: GetRouter(request),
+ routeValuesFactory: GetRouteValuesFactory(request));
+
+ RouteValueDictionary result = await transformer.TransformAsync(new DefaultHttpContext(), new RouteValueDictionary());
+
+ Assert.AreEqual(routeValues.ControllerName, result["controller"]);
+ Assert.AreEqual(routeValues.ActionName, result["action"]);
+ }
+
+ private class TestController : RenderController
+ {
+ public TestController(ILogger logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
+ : base(logger, compositeViewEngine, umbracoContextAccessor)
+ {
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactoryTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactoryTests.cs
new file mode 100644
index 0000000000..17ce59862f
--- /dev/null
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactoryTests.cs
@@ -0,0 +1,86 @@
+using System;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Logging.Abstractions;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.Services;
+using Umbraco.Core.Strings;
+using Umbraco.Web.Common.Routing;
+using Umbraco.Web.Features;
+using Umbraco.Web.Routing;
+using Umbraco.Web.Website.Controllers;
+using Umbraco.Web.Website.Routing;
+
+namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Routing
+{
+
+ [TestFixture]
+ public class UmbracoRouteValuesFactoryTests
+ {
+ private UmbracoRouteValuesFactory GetFactory(out Mock publishedRouter, out UmbracoRenderingDefaults renderingDefaults)
+ {
+ var builder = new PublishedRequestBuilder(new Uri("https://example.com"), Mock.Of());
+ builder.SetPublishedContent(Mock.Of());
+ IPublishedRequest request = builder.Build();
+
+ publishedRouter = new Mock();
+ publishedRouter.Setup(x => x.UpdateRequestToNotFound(It.IsAny()))
+ .Returns((IPublishedRequest r) => builder)
+ .Verifiable();
+
+ renderingDefaults = new UmbracoRenderingDefaults();
+
+ var factory = new UmbracoRouteValuesFactory(
+ renderingDefaults,
+ Mock.Of(),
+ new UmbracoFeatures(),
+ new HijackedRouteEvaluator(
+ new NullLogger(),
+ Mock.Of()),
+ publishedRouter.Object);
+
+ return factory;
+ }
+
+ [Test]
+ public void Update_Request_To_Not_Found_When_No_Template()
+ {
+ var builder = new PublishedRequestBuilder(new Uri("https://example.com"), Mock.Of());
+ builder.SetPublishedContent(Mock.Of());
+ IPublishedRequest request = builder.Build();
+
+ UmbracoRouteValuesFactory factory = GetFactory(out Mock publishedRouter, out _);
+
+ UmbracoRouteValues result = factory.Create(new DefaultHttpContext(), new RouteValueDictionary(), request);
+
+ // The request has content, no template, no hijacked route and no disabled template features so UpdateRequestToNotFound will be called
+ publishedRouter.Verify(m => m.UpdateRequestToNotFound(It.IsAny()), Times.Once);
+ }
+
+ [Test]
+ public void Adds_Result_To_Route_Value_Dictionary()
+ {
+ var builder = new PublishedRequestBuilder(new Uri("https://example.com"), Mock.Of());
+ builder.SetPublishedContent(Mock.Of());
+ builder.SetTemplate(Mock.Of());
+ IPublishedRequest request = builder.Build();
+
+ UmbracoRouteValuesFactory factory = GetFactory(out _, out UmbracoRenderingDefaults renderingDefaults);
+
+ var routeVals = new RouteValueDictionary();
+ UmbracoRouteValues result = factory.Create(new DefaultHttpContext(), routeVals, request);
+
+ Assert.IsNotNull(result);
+ Assert.IsTrue(routeVals.ContainsKey(Constants.Web.UmbracoRouteDefinitionDataToken));
+ Assert.AreEqual(result, routeVals[Constants.Web.UmbracoRouteDefinitionDataToken]);
+ Assert.AreEqual(renderingDefaults.DefaultControllerType, result.ControllerType);
+ Assert.AreEqual(UmbracoRouteValues.DefaultActionName, result.ActionName);
+ Assert.IsNull(result.TemplateName);
+ }
+ }
+}
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index e1f57262ff..fe9d41f681 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -112,7 +112,7 @@
-
+
@@ -307,7 +307,7 @@
-
+
diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs
index c5104c0fdc..cd38712aa0 100644
--- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs
+++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
@@ -61,7 +61,7 @@ namespace Umbraco.Web.Common.AspNetCore
public Uri GetApplicationUrl()
{
- //Fixme: This causes problems with site swap on azure because azure pre-warms a site by calling into `localhost` and when it does that
+ // Fixme: This causes problems with site swap on azure because azure pre-warms a site by calling into `localhost` and when it does that
// it changes the URL to `localhost:80` which actually doesn't work for pinging itself, it only works internally in Azure. The ironic part
// about this is that this is here specifically for the slot swap scenario https://issues.umbraco.org/issue/U4-10626
diff --git a/src/Umbraco.Web.Common/Localization/UmbracoBackOfficeIdentityCultureProvider.cs b/src/Umbraco.Web.Common/Localization/UmbracoBackOfficeIdentityCultureProvider.cs
index 741583413c..e2a7c35daa 100644
--- a/src/Umbraco.Web.Common/Localization/UmbracoBackOfficeIdentityCultureProvider.cs
+++ b/src/Umbraco.Web.Common/Localization/UmbracoBackOfficeIdentityCultureProvider.cs
@@ -15,6 +15,7 @@ namespace Umbraco.Web.Common.Localization
public class UmbracoBackOfficeIdentityCultureProvider : RequestCultureProvider
{
private readonly RequestLocalizationOptions _localizationOptions;
+ private readonly object _locker = new object();
///
/// Initializes a new instance of the class.
@@ -31,18 +32,21 @@ namespace Umbraco.Web.Common.Localization
return NullProviderCultureResult;
}
- // We need to dynamically change the supported cultures since we won't ever know what languages are used since
- // they are dynamic within Umbraco.
- var cultureExists = _localizationOptions.SupportedCultures.Contains(culture);
-
- if (!cultureExists)
+ lock(_locker)
{
- // add this as a supporting culture
- _localizationOptions.SupportedCultures.Add(culture);
- _localizationOptions.SupportedUICultures.Add(culture);
- }
+ // We need to dynamically change the supported cultures since we won't ever know what languages are used since
+ // they are dynamic within Umbraco.
+ var cultureExists = _localizationOptions.SupportedCultures.Contains(culture);
- return Task.FromResult(new ProviderCultureResult(culture.Name));
+ if (!cultureExists)
+ {
+ // add this as a supporting culture
+ _localizationOptions.SupportedCultures.Add(culture);
+ _localizationOptions.SupportedUICultures.Add(culture);
+ }
+
+ return Task.FromResult(new ProviderCultureResult(culture.Name));
+ }
}
}
}
diff --git a/src/Umbraco.Web.Common/Localization/UmbracoPublishedContentCultureProvider.cs b/src/Umbraco.Web.Common/Localization/UmbracoPublishedContentCultureProvider.cs
index bedf5e73a7..7322ad2869 100644
--- a/src/Umbraco.Web.Common/Localization/UmbracoPublishedContentCultureProvider.cs
+++ b/src/Umbraco.Web.Common/Localization/UmbracoPublishedContentCultureProvider.cs
@@ -19,6 +19,7 @@ namespace Umbraco.Web.Common.Localization
public class UmbracoPublishedContentCultureProvider : RequestCultureProvider
{
private readonly RequestLocalizationOptions _localizationOptions;
+ private readonly object _locker = new object();
///
/// Initializes a new instance of the class.
@@ -33,19 +34,22 @@ namespace Umbraco.Web.Common.Localization
string culture = routeValues.PublishedRequest?.Culture;
if (culture != null)
{
- // We need to dynamically change the supported cultures since we won't ever know what languages are used since
- // they are dynamic within Umbraco.
- // This code to check existence is borrowed from aspnetcore to avoid creating a CultureInfo
- // https://github.com/dotnet/aspnetcore/blob/b795ac3546eb3e2f47a01a64feb3020794ca33bb/src/Middleware/Localization/src/RequestLocalizationMiddleware.cs#L165
- CultureInfo existingCulture = _localizationOptions.SupportedCultures.FirstOrDefault(supportedCulture =>
- StringSegment.Equals(supportedCulture.Name, culture, StringComparison.OrdinalIgnoreCase));
-
- if (existingCulture == null)
+ lock (_locker)
{
- // add this as a supporting culture
- var ci = CultureInfo.GetCultureInfo(culture);
- _localizationOptions.SupportedCultures.Add(ci);
- _localizationOptions.SupportedUICultures.Add(ci);
+ // We need to dynamically change the supported cultures since we won't ever know what languages are used since
+ // they are dynamic within Umbraco.
+ // This code to check existence is borrowed from aspnetcore to avoid creating a CultureInfo
+ // https://github.com/dotnet/aspnetcore/blob/b795ac3546eb3e2f47a01a64feb3020794ca33bb/src/Middleware/Localization/src/RequestLocalizationMiddleware.cs#L165
+ CultureInfo existingCulture = _localizationOptions.SupportedCultures.FirstOrDefault(supportedCulture =>
+ StringSegment.Equals(supportedCulture.Name, culture, StringComparison.OrdinalIgnoreCase));
+
+ if (existingCulture == null)
+ {
+ // add this as a supporting culture
+ var ci = CultureInfo.GetCultureInfo(culture);
+ _localizationOptions.SupportedCultures.Add(ci);
+ _localizationOptions.SupportedUICultures.Add(ci);
+ }
}
return Task.FromResult(new ProviderCultureResult(culture));
diff --git a/src/Umbraco.Web.Common/Routing/IRoutableDocumentFilter.cs b/src/Umbraco.Web.Common/Routing/IRoutableDocumentFilter.cs
new file mode 100644
index 0000000000..b921918bf6
--- /dev/null
+++ b/src/Umbraco.Web.Common/Routing/IRoutableDocumentFilter.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Web.Common.Routing
+{
+ public interface IRoutableDocumentFilter
+ {
+ bool IsDocumentRequest(string absPath);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs b/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
index dee90bbfba..18190f9ad9 100644
--- a/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
+++ b/src/Umbraco.Web.Common/Routing/RoutableDocumentFilter.cs
@@ -20,7 +20,7 @@ namespace Umbraco.Web.Common.Routing
///
/// There are various checks to determine if this is a front-end request such as checking if the request is part of any reserved paths or existing MVC routes.
///
- public sealed class RoutableDocumentFilter
+ public sealed class RoutableDocumentFilter : IRoutableDocumentFilter
{
private readonly ConcurrentDictionary _routeChecks = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
private readonly GlobalSettings _globalSettings;
diff --git a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs
index f7d3e61664..e89a874d71 100644
--- a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs
+++ b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs
@@ -16,6 +16,7 @@ namespace Umbraco.Web
public class UmbracoContext : DisposableObjectSlim, IDisposeOnRequestEnd, IUmbracoContext
{
private readonly IHostingEnvironment _hostingEnvironment;
+ private readonly UriUtility _uriUtility;
private readonly ICookieManager _cookieManager;
private readonly IRequestAccessor _requestAccessor;
private readonly Lazy _publishedSnapshot;
@@ -23,6 +24,8 @@ namespace Umbraco.Web
private bool? _previewing;
private readonly IBackOfficeSecurity _backofficeSecurity;
private readonly UmbracoRequestPaths _umbracoRequestPaths;
+ private Uri _originalRequestUrl;
+ private Uri _cleanedUmbracoUrl;
// initializes a new instance of the UmbracoContext class
// internal for unit tests
@@ -43,8 +46,8 @@ namespace Umbraco.Web
throw new ArgumentNullException(nameof(publishedSnapshotService));
}
- VariationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
-
+ VariationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
+ _uriUtility = uriUtility;
_hostingEnvironment = hostingEnvironment;
_cookieManager = cookieManager;
_requestAccessor = requestAccessor;
@@ -56,15 +59,6 @@ namespace Umbraco.Web
// beware - we cannot expect a current user here, so detecting preview mode must be a lazy thing
_publishedSnapshot = new Lazy(() => publishedSnapshotService.CreatePublishedSnapshot(PreviewToken));
-
- // set the urls...
- // NOTE: The request will not be available during app startup so we can only set this to an absolute URL of localhost, this
- // is a work around to being able to access the UmbracoContext during application startup and this will also ensure that people
- // 'could' still generate URLs during startup BUT any domain driven URL generation will not work because it is NOT possible to get
- // the current domain during application startup.
- // see: http://issues.umbraco.org/issue/U4-1890
- OriginalRequestUrl = _requestAccessor.GetRequestUrl() ?? new Uri("http://localhost");
- CleanedUmbracoUrl = uriUtility.UriToUmbraco(OriginalRequestUrl);
}
///
@@ -79,10 +73,17 @@ namespace Umbraco.Web
internal Guid UmbracoRequestId { get; }
///
- public Uri OriginalRequestUrl { get; }
+ // set the urls lazily, no need to allocate until they are needed...
+ // NOTE: The request will not be available during app startup so we can only set this to an absolute URL of localhost, this
+ // is a work around to being able to access the UmbracoContext during application startup and this will also ensure that people
+ // 'could' still generate URLs during startup BUT any domain driven URL generation will not work because it is NOT possible to get
+ // the current domain during application startup.
+ // see: http://issues.umbraco.org/issue/U4-1890
+ public Uri OriginalRequestUrl => _originalRequestUrl ?? (_originalRequestUrl = _requestAccessor.GetRequestUrl() ?? new Uri("http://localhost"));
///
- public Uri CleanedUmbracoUrl { get; }
+ // set the urls lazily, no need to allocate until they are needed...
+ public Uri CleanedUmbracoUrl => _cleanedUmbracoUrl ?? (_cleanedUmbracoUrl = _uriUtility.UriToUmbraco(OriginalRequestUrl));
///
public IPublishedSnapshot PublishedSnapshot => _publishedSnapshot.Value;
diff --git a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs
index a641f32235..ca2f9e6161 100644
--- a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs
+++ b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs
@@ -41,7 +41,7 @@ namespace Umbraco.Web.Website.DependencyInjection
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
- builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
builder.AddDistributedCache();
diff --git a/src/Umbraco.Web.Website/Routing/HijackedRouteEvaluator.cs b/src/Umbraco.Web.Website/Routing/HijackedRouteEvaluator.cs
index a72268d298..79036a01e1 100644
--- a/src/Umbraco.Web.Website/Routing/HijackedRouteEvaluator.cs
+++ b/src/Umbraco.Web.Website/Routing/HijackedRouteEvaluator.cs
@@ -50,14 +50,22 @@ namespace Umbraco.Web.Website.Routing
&& TypeHelper.IsTypeAssignableFrom(controllerDescriptor.ControllerTypeInfo))
{
// now check if the custom action matches
- var customActionExists = action != null && customControllerCandidates.Any(x => x.ActionName.InvariantEquals(action));
+ var resultingAction = DefaultActionName;
+ if (action != null)
+ {
+ var found = customControllerCandidates.FirstOrDefault(x => x.ActionName.InvariantEquals(action))?.ActionName;
+ if (found != null)
+ {
+ resultingAction = found;
+ }
+ }
// it's a hijacked route with a custom controller, so return the the values
return new HijackedRouteResult(
true,
controllerDescriptor.ControllerName,
controllerDescriptor.ControllerTypeInfo,
- customActionExists ? action : DefaultActionName);
+ resultingAction);
}
else
{
diff --git a/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs b/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs
index 5d0c564df3..af23105099 100644
--- a/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs
+++ b/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs
@@ -34,7 +34,7 @@ namespace Umbraco.Web.Website.Routing
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IRuntimeState _runtime;
private readonly IUmbracoRouteValuesFactory _routeValuesFactory;
- private readonly RoutableDocumentFilter _routableDocumentFilter;
+ private readonly IRoutableDocumentFilter _routableDocumentFilter;
///
/// Initializes a new instance of the class.
@@ -47,16 +47,21 @@ namespace Umbraco.Web.Website.Routing
IHostingEnvironment hostingEnvironment,
IRuntimeState runtime,
IUmbracoRouteValuesFactory routeValuesFactory,
- RoutableDocumentFilter routableDocumentFilter)
+ IRoutableDocumentFilter routableDocumentFilter)
{
- _logger = logger;
- _umbracoContextAccessor = umbracoContextAccessor;
- _publishedRouter = publishedRouter;
+ if (globalSettings is null)
+ {
+ throw new System.ArgumentNullException(nameof(globalSettings));
+ }
+
+ _logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
+ _umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
+ _publishedRouter = publishedRouter ?? throw new System.ArgumentNullException(nameof(publishedRouter));
_globalSettings = globalSettings.Value;
- _hostingEnvironment = hostingEnvironment;
- _runtime = runtime;
- _routeValuesFactory = routeValuesFactory;
- _routableDocumentFilter = routableDocumentFilter;
+ _hostingEnvironment = hostingEnvironment ?? throw new System.ArgumentNullException(nameof(hostingEnvironment));
+ _runtime = runtime ?? throw new System.ArgumentNullException(nameof(runtime));
+ _routeValuesFactory = routeValuesFactory ?? throw new System.ArgumentNullException(nameof(routeValuesFactory));
+ _routableDocumentFilter = routableDocumentFilter ?? throw new System.ArgumentNullException(nameof(routableDocumentFilter));
}
///
@@ -113,9 +118,10 @@ namespace Umbraco.Web.Website.Routing
// an immutable object. The only way to make this better would be to have a RouteRequest
// as part of UmbracoContext but then it will require a PublishedRouter dependency so not sure that's worth it.
// Maybe could be a one-time Set method instead?
- IPublishedRequest publishedRequest = umbracoContext.PublishedRequest = await _publishedRouter.RouteRequestAsync(requestBuilder, new RouteRequestOptions(RouteDirection.Inbound));
+ IPublishedRequest routedRequest = await _publishedRouter.RouteRequestAsync(requestBuilder, new RouteRequestOptions(RouteDirection.Inbound));
+ umbracoContext.PublishedRequest = routedRequest;
- return publishedRequest;
+ return routedRequest;
}
}
}
diff --git a/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs b/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs
index fd92f7f11e..d26216204e 100644
--- a/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs
+++ b/src/Umbraco.Web.Website/Routing/UmbracoRouteValuesFactory.cs
@@ -103,7 +103,7 @@ namespace Umbraco.Web.Website.Routing
{
IPublishedRequest request = def.PublishedRequest;
- var customControllerName = request.PublishedContent?.ContentType.Alias;
+ var customControllerName = request.PublishedContent?.ContentType?.Alias;
if (customControllerName != null)
{
HijackedRouteResult hijackedResult = _hijackedRouteEvaluator.Evaluate(customControllerName, def.TemplateName);
@@ -144,6 +144,12 @@ namespace Umbraco.Web.Website.Routing
// We then need to re-run this through the pipeline for the last
// chance finders to work.
IPublishedRequestBuilder builder = _publishedRouter.UpdateRequestToNotFound(request);
+
+ if (builder == null)
+ {
+ throw new InvalidOperationException($"The call to {nameof(IPublishedRouter.UpdateRequestToNotFound)} cannot return null");
+ }
+
request = builder.Build();
def = new UmbracoRouteValues(
diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs
index 7461364d3f..92a480bc45 100644
--- a/src/Umbraco.Web/UmbracoContext.cs
+++ b/src/Umbraco.Web/UmbracoContext.cs
@@ -62,7 +62,6 @@ namespace Umbraco.Web
// 'could' still generate URLs during startup BUT any domain driven URL generation will not work because it is NOT possible to get
// the current domain during application startup.
// see: http://issues.umbraco.org/issue/U4-1890
- //
OriginalRequestUrl = GetRequestFromContext()?.Url ?? new Uri("http://localhost");
CleanedUmbracoUrl = uriUtility.UriToUmbraco(OriginalRequestUrl);
}