diff --git a/src/Umbraco.Web.Website/Routing/EagerMatcherPolicy.cs b/src/Umbraco.Web.Website/Routing/EagerMatcherPolicy.cs index bb5cb52a4d57..b6cd4a361522 100644 --- a/src/Umbraco.Web.Website/Routing/EagerMatcherPolicy.cs +++ b/src/Umbraco.Web.Website/Routing/EagerMatcherPolicy.cs @@ -8,7 +8,9 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; using Umbraco.Cms.Web.Common.Controllers; +using Umbraco.Cms.Web.Common.Routing; using Umbraco.Cms.Web.Website.Controllers; using Umbraco.Extensions; @@ -37,6 +39,8 @@ internal class EagerMatcherPolicy : MatcherPolicy, IEndpointSelectorPolicy private readonly IRuntimeState _runtimeState; private readonly EndpointDataSource _endpointDataSource; private readonly UmbracoRequestPaths _umbracoRequestPaths; + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IPublishedRouter _publishedRouter; private GlobalSettings _globalSettings; private readonly Lazy _installEndpoint; private readonly Lazy _renderEndpoint; @@ -45,11 +49,15 @@ public EagerMatcherPolicy( IRuntimeState runtimeState, EndpointDataSource endpointDataSource, UmbracoRequestPaths umbracoRequestPaths, - IOptionsMonitor globalSettings) + IOptionsMonitor globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + IPublishedRouter publishedRouter) { _runtimeState = runtimeState; _endpointDataSource = endpointDataSource; _umbracoRequestPaths = umbracoRequestPaths; + _umbracoContextAccessor = umbracoContextAccessor; + _publishedRouter = publishedRouter; _globalSettings = globalSettings.CurrentValue; globalSettings.OnChange(settings => _globalSettings = settings); _installEndpoint = new Lazy(GetInstallEndpoint); @@ -112,11 +120,22 @@ public async Task ApplyAsync(HttpContext httpContext, CandidateSet candidates) ControllerActionDescriptor? controllerDescriptor = routeEndpoint.Metadata.GetMetadata(); TypeInfo? controllerTypeInfo = controllerDescriptor?.ControllerTypeInfo; if (controllerTypeInfo is not null && - (controllerTypeInfo.IsType() || controllerTypeInfo.IsType())) + (controllerTypeInfo.IsType() + || controllerTypeInfo.IsType())) { return; } + // If it's an UmbracoPageController we need to do some domain routing. + // We need to do this in oder to handle cultures for our Dictionary. + // This is because UmbracoPublishedContentCultureProvider is ued to set the Thread.CurrentThread.CurrentUICulture + // The CultureProvider is run before the actual routing, this means that our UmbracoVirtualPageFilterAttribute is hit AFTER the culture is set. + // Meaning we have to route the domain part already now, this is not pretty, but it beats having to look for content we know doesn't exist. + if (controllerTypeInfo is not null && controllerTypeInfo.IsType()) + { + await RouteVirtualRequestAsync(httpContext); + } + if (routeEndpoint.Order < lowestOrder) { // We have to ensure that the route is valid for the current request method. @@ -153,6 +172,22 @@ public async Task ApplyAsync(HttpContext httpContext, CandidateSet candidates) } } + private async Task RouteVirtualRequestAsync(HttpContext context) + { + if (_umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? umbracoContext) is false) + { + return; + } + + IPublishedRequestBuilder requestBuilder = + await _publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + _publishedRouter.RouteDomain(requestBuilder); + // This is just a temporary RouteValues object just for culture which will be overwritten later + // so we can just use a dummy action descriptor. + var umbracoRouteValues = new UmbracoRouteValues(requestBuilder.Build(), new ControllerActionDescriptor()); + context.Features.Set(umbracoRouteValues); + } + /// /// Replaces the first endpoint candidate with the specified endpoint, invalidating all other candidates, /// guaranteeing that the specified endpoint will be hit.