From 5ae5fe338a96098c8bb3f97cdffb423b5c352e56 Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 7 Jun 2024 12:49:13 +0200 Subject: [PATCH] V13: Set request culture for VirtualPageController (#16572) * Rename FindDomain to FindAndSetDomain * Ensure VariationContext and PublishedRequest is updated for virtual page controller --- src/Umbraco.Core/Routing/IPublishedRouter.cs | 33 ++++++++++++++ src/Umbraco.Core/Routing/PublishedRouter.cs | 43 ++++++++++++++----- .../UmbracoVirtualPageFilterAttribute.cs | 8 ++++ .../Routing/UmbracoVirtualPageRoute.cs | 1 + .../ContentFinderByAliasWithDomainsTests.cs | 2 +- .../ContentFinderByUrlWithDomainsTests.cs | 4 +- .../Routing/DomainsAndCulturesTests.cs | 6 +-- .../Routing/UrlsWithNestedDomains.cs | 2 +- 8 files changed, 82 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Core/Routing/IPublishedRouter.cs b/src/Umbraco.Core/Routing/IPublishedRouter.cs index 53a07ff325..a01e17c94d 100644 --- a/src/Umbraco.Core/Routing/IPublishedRouter.cs +++ b/src/Umbraco.Core/Routing/IPublishedRouter.cs @@ -47,4 +47,37 @@ public interface IPublishedRouter /// /// Task UpdateRequestAsync(IPublishedRequest request, IPublishedContent? publishedContent); + + /// + /// Finds the site root (if any) matching the http request, and updates the PublishedRequest and VariationContext accordingly. + /// + /// + /// This method is used for VirtualPage routing. + /// + /// + /// In this case we do not want to run the entire routing pipeline since ContentFinders are not needed here. + /// However, we do want to set the culture on VariationContext and PublishedRequest to the values specified by the domains. + /// + /// + /// + /// The request to update the culture on domain on + /// True if a domain was found otherwise false. + bool RouteDomain(IPublishedRequestBuilder request) => false; + + /// + /// Finds the site root (if any) matching the http request, and updates the VariationContext accordingly. + /// + /// + /// + /// This is used for VirtualPage routing. + /// + /// + /// This is required to set the culture on VariationContext to the values specified by the domains, before the FindContent method is called. + /// In order to allow the FindContent implementer to correctly find content based off the culture. Before the PublishedRequest is built. + /// + /// + /// The URI to resolve the domain from. + /// True if a domain was found, otherwise false. + bool UpdateVariationContext(Uri uri) => false; + } diff --git a/src/Umbraco.Core/Routing/PublishedRouter.cs b/src/Umbraco.Core/Routing/PublishedRouter.cs index f04fd04ca2..df1d459327 100644 --- a/src/Umbraco.Core/Routing/PublishedRouter.cs +++ b/src/Umbraco.Core/Routing/PublishedRouter.cs @@ -108,7 +108,7 @@ public class PublishedRouter : IPublishedRouter // find domain if (builder.Domain == null) { - FindDomain(builder); + FindAndSetDomain(builder); } await RouteRequestInternalAsync(builder); @@ -185,7 +185,7 @@ public class PublishedRouter : IPublishedRouter private async Task TryRouteRequest(IPublishedRequestBuilder request) { - FindDomain(request); + FindAndSetDomain(request); if (request.IsRedirect()) { @@ -270,18 +270,31 @@ public class PublishedRouter : IPublishedRouter // to find out the appropriate template } - /// - /// Finds the site root (if any) matching the http request, and updates the PublishedRequest accordingly. - /// - /// A value indicating whether a domain was found. - internal bool FindDomain(IPublishedRequestBuilder request) + /// + public bool RouteDomain(IPublishedRequestBuilder request) + { + var found = FindAndSetDomain(request); + HandleWildcardDomains(request); + SetVariationContext(request.Culture); + return found; + } + + /// + public bool UpdateVariationContext(Uri uri) + { + DomainAndUri? domain = FindDomain(uri, out _); + SetVariationContext(domain?.Culture); + return domain?.Culture is not null; + } + + private DomainAndUri? FindDomain(Uri uri, out string? defaultCulture) { const string tracePrefix = "FindDomain: "; // note - we are not handling schemes nor ports here. if (_logger.IsEnabled(LogLevel.Debug)) { - _logger.LogDebug("{TracePrefix}Uri={RequestUri}", tracePrefix, request.Uri); + _logger.LogDebug("{TracePrefix}Uri={RequestUri}", tracePrefix, uri); } IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); @@ -315,10 +328,20 @@ public class PublishedRouter : IPublishedRouter domains = domains?.Where(IsPublishedContentDomain).ToList(); - var defaultCulture = domainsCache?.DefaultCulture; + defaultCulture = domainsCache?.DefaultCulture; + return DomainUtilities.SelectDomain(domains, uri, defaultCulture: defaultCulture); + } + + /// + /// Finds the site root (if any) matching the http request, and updates the PublishedRequest accordingly. + /// + /// A value indicating whether a domain was found. + internal bool FindAndSetDomain(IPublishedRequestBuilder request) + { + const string tracePrefix = "FindDomain: "; // try to find a domain matching the current request - DomainAndUri? domainAndUri = DomainUtilities.SelectDomain(domains, request.Uri, defaultCulture: defaultCulture); + DomainAndUri? domainAndUri = FindDomain(request.Uri, out var defaultCulture); // handle domain - always has a contentId and a culture if (domainAndUri != null) diff --git a/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs b/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs index 707dfe0b8d..fadd0d19e2 100644 --- a/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs +++ b/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs @@ -1,9 +1,11 @@ using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Web.Common.Controllers; using Umbraco.Cms.Web.Common.Routing; @@ -40,6 +42,12 @@ public class UmbracoVirtualPageFilterAttribute : Attribute, IAsyncActionFilter if (endpoint != null) { IUmbracoVirtualPageRoute umbracoVirtualPageRoute = context.HttpContext.RequestServices.GetRequiredService(); + IPublishedRouter publishedRouter = context.HttpContext.RequestServices.GetRequiredService(); + UriUtility uriUtility = context.HttpContext.RequestServices.GetRequiredService(); + + var originalRequestUrl = new Uri(context.HttpContext.Request.GetEncodedUrl()); + Uri cleanedUri = uriUtility.UriToUmbraco(originalRequestUrl); + publishedRouter.UpdateVariationContext(cleanedUri); IPublishedContent? publishedContent = umbracoVirtualPageRoute.FindContent(endpoint, context); diff --git a/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs b/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs index cff5a589f6..9813cf3212 100644 --- a/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs +++ b/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs @@ -155,6 +155,7 @@ public class UmbracoVirtualPageRoute : IUmbracoVirtualPageRoute IPublishedRequestBuilder requestBuilder = await _publishedRouter.CreateRequestAsync(cleanedUrl); requestBuilder.SetPublishedContent(publishedContent); + _publishedRouter.RouteDomain(requestBuilder); return requestBuilder.Build(); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasWithDomainsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasWithDomainsTests.cs index 3f639965cd..30bb4ae70c 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasWithDomainsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasWithDomainsTests.cs @@ -31,7 +31,7 @@ public class ContentFinderByAliasWithDomainsTests : UrlRoutingTestBase var request = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // must lookup domain - publishedRouter.FindDomain(request); + publishedRouter.FindAndSetDomain(request); if (expectedNode > 0) { diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlWithDomainsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlWithDomainsTests.cs index c9069046ac..4606641265 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlWithDomainsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlWithDomainsTests.cs @@ -207,7 +207,7 @@ public class ContentFinderByUrlWithDomainsTests : UrlRoutingTestBase var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // must lookup domain else lookup by URL fails - publishedRouter.FindDomain(frequest); + publishedRouter.FindAndSetDomain(frequest); var lookup = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); var result = await lookup.TryFindContent(frequest); @@ -245,7 +245,7 @@ public class ContentFinderByUrlWithDomainsTests : UrlRoutingTestBase var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // must lookup domain else lookup by URL fails - publishedRouter.FindDomain(frequest); + publishedRouter.FindAndSetDomain(frequest); Assert.AreEqual(expectedCulture, frequest.Culture); var lookup = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/DomainsAndCulturesTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/DomainsAndCulturesTests.cs index c54e540864..3945e2346d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/DomainsAndCulturesTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/DomainsAndCulturesTests.cs @@ -261,7 +261,7 @@ public class DomainsAndCulturesTests : UrlRoutingTestBase var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // lookup domain - publishedRouter.FindDomain(frequest); + publishedRouter.FindAndSetDomain(frequest); Assert.AreEqual(expectedCulture, frequest.Culture); @@ -310,7 +310,7 @@ public class DomainsAndCulturesTests : UrlRoutingTestBase var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // lookup domain - publishedRouter.FindDomain(frequest); + publishedRouter.FindAndSetDomain(frequest); // find document var finder = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); @@ -345,7 +345,7 @@ public class DomainsAndCulturesTests : UrlRoutingTestBase var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // lookup domain - publishedRouter.FindDomain(frequest); + publishedRouter.FindAndSetDomain(frequest); Assert.IsNotNull(frequest.Domain); Assert.AreEqual(expectedCulture, frequest.Culture); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsWithNestedDomains.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsWithNestedDomains.cs index 9edce34707..d0536640e2 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsWithNestedDomains.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsWithNestedDomains.cs @@ -62,7 +62,7 @@ public class UrlsWithNestedDomains : UrlRoutingTestBase var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - publishedRouter.FindDomain(frequest); + publishedRouter.FindAndSetDomain(frequest); Assert.IsTrue(frequest.HasDomain()); // check that it's been routed