88using Umbraco . Cms . Core . Configuration . Models ;
99using Umbraco . Cms . Core . Routing ;
1010using Umbraco . Cms . Core . Services ;
11+ using Umbraco . Cms . Core . Web ;
1112using Umbraco . Cms . Web . Common . Controllers ;
13+ using Umbraco . Cms . Web . Common . Routing ;
1214using Umbraco . Cms . Web . Website . Controllers ;
1315using Umbraco . Extensions ;
1416
@@ -37,6 +39,8 @@ internal class EagerMatcherPolicy : MatcherPolicy, IEndpointSelectorPolicy
3739 private readonly IRuntimeState _runtimeState ;
3840 private readonly EndpointDataSource _endpointDataSource ;
3941 private readonly UmbracoRequestPaths _umbracoRequestPaths ;
42+ private readonly IUmbracoContextAccessor _umbracoContextAccessor ;
43+ private readonly IPublishedRouter _publishedRouter ;
4044 private GlobalSettings _globalSettings ;
4145 private readonly Lazy < Endpoint > _installEndpoint ;
4246 private readonly Lazy < Endpoint > _renderEndpoint ;
@@ -45,11 +49,15 @@ public EagerMatcherPolicy(
4549 IRuntimeState runtimeState ,
4650 EndpointDataSource endpointDataSource ,
4751 UmbracoRequestPaths umbracoRequestPaths ,
48- IOptionsMonitor < GlobalSettings > globalSettings )
52+ IOptionsMonitor < GlobalSettings > globalSettings ,
53+ IUmbracoContextAccessor umbracoContextAccessor ,
54+ IPublishedRouter publishedRouter )
4955 {
5056 _runtimeState = runtimeState ;
5157 _endpointDataSource = endpointDataSource ;
5258 _umbracoRequestPaths = umbracoRequestPaths ;
59+ _umbracoContextAccessor = umbracoContextAccessor ;
60+ _publishedRouter = publishedRouter ;
5361 _globalSettings = globalSettings . CurrentValue ;
5462 globalSettings . OnChange ( settings => _globalSettings = settings ) ;
5563 _installEndpoint = new Lazy < Endpoint > ( GetInstallEndpoint ) ;
@@ -112,11 +120,22 @@ public async Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
112120 ControllerActionDescriptor ? controllerDescriptor = routeEndpoint . Metadata . GetMetadata < ControllerActionDescriptor > ( ) ;
113121 TypeInfo ? controllerTypeInfo = controllerDescriptor ? . ControllerTypeInfo ;
114122 if ( controllerTypeInfo is not null &&
115- ( controllerTypeInfo . IsType < RenderController > ( ) || controllerTypeInfo . IsType < SurfaceController > ( ) ) )
123+ ( controllerTypeInfo . IsType < RenderController > ( )
124+ || controllerTypeInfo . IsType < SurfaceController > ( ) ) )
116125 {
117126 return ;
118127 }
119128
129+ // If it's an UmbracoPageController we need to do some domain routing.
130+ // We need to do this in oder to handle cultures for our Dictionary.
131+ // This is because UmbracoPublishedContentCultureProvider is ued to set the Thread.CurrentThread.CurrentUICulture
132+ // The CultureProvider is run before the actual routing, this means that our UmbracoVirtualPageFilterAttribute is hit AFTER the culture is set.
133+ // 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.
134+ if ( controllerTypeInfo is not null && controllerTypeInfo . IsType < UmbracoPageController > ( ) )
135+ {
136+ await RouteVirtualRequestAsync ( httpContext ) ;
137+ }
138+
120139 if ( routeEndpoint . Order < lowestOrder )
121140 {
122141 // 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)
153172 }
154173 }
155174
175+ private async Task RouteVirtualRequestAsync ( HttpContext context )
176+ {
177+ if ( _umbracoContextAccessor . TryGetUmbracoContext ( out IUmbracoContext ? umbracoContext ) is false )
178+ {
179+ return ;
180+ }
181+
182+ IPublishedRequestBuilder requestBuilder =
183+ await _publishedRouter . CreateRequestAsync ( umbracoContext . CleanedUmbracoUrl ) ;
184+ _publishedRouter . RouteDomain ( requestBuilder ) ;
185+ // This is just a temporary RouteValues object just for culture which will be overwritten later
186+ // so we can just use a dummy action descriptor.
187+ var umbracoRouteValues = new UmbracoRouteValues ( requestBuilder . Build ( ) , new ControllerActionDescriptor ( ) ) ;
188+ context . Features . Set ( umbracoRouteValues ) ;
189+ }
190+
156191 /// <summary>
157192 /// Replaces the first endpoint candidate with the specified endpoint, invalidating all other candidates,
158193 /// guaranteeing that the specified endpoint will be hit.
0 commit comments