From f28b4c1279201051583ff0ed78cc99f9409e8d05 Mon Sep 17 00:00:00 2001 From: Justin Neville <67802060+justin-nevitech@users.noreply.github.com> Date: Tue, 6 Dec 2022 11:39:43 +0000 Subject: [PATCH] Fix for issue 13017 - BeginUmbracoForm doesn't work with custom umbraco routes (#13103) * Fix issue with custom Umbraco routes not working after submitting to a Surface controller * Added comments * Fixed breaking changes * Fixed test by using correct new ctor * Fixed initializtion of UmbracoRouteValueTransformer due to ambiguous ctor Co-authored-by: Bjarke Berg --- .../EndpointDataSourceExtensions.cs | 35 ++++ .../Extensions/EndpointExtensions.cs | 41 ++++ .../Extensions/LinkParserExtensions.cs | 31 +++ .../UmbracoVirtualPageFilterAttribute.cs | 71 +++---- .../Routing/IUmbracoVirtualPageRoute.cs | 64 +++++++ .../Routing/UmbracoVirtualPageRoute.cs | 178 ++++++++++++++++++ .../UmbracoBuilderExtensions.cs | 19 +- .../Routing/UmbracoRouteValueTransformer.cs | 31 ++- .../UmbracoRouteValueTransformerTests.cs | 12 +- 9 files changed, 432 insertions(+), 50 deletions(-) create mode 100644 src/Umbraco.Web.Common/Extensions/EndpointDataSourceExtensions.cs create mode 100644 src/Umbraco.Web.Common/Extensions/EndpointExtensions.cs create mode 100644 src/Umbraco.Web.Common/Extensions/LinkParserExtensions.cs create mode 100644 src/Umbraco.Web.Common/Routing/IUmbracoVirtualPageRoute.cs create mode 100644 src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs diff --git a/src/Umbraco.Web.Common/Extensions/EndpointDataSourceExtensions.cs b/src/Umbraco.Web.Common/Extensions/EndpointDataSourceExtensions.cs new file mode 100644 index 0000000000..f1d66a25c5 --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/EndpointDataSourceExtensions.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Umbraco.Cms.Web.Common.Extensions; + +/// +/// Extensions methods for EndpointDataSource. +/// +public static class EndpointDataSourceExtensions +{ + /// + /// Gets an endpoint that matches the specified path. + /// + /// The end point data source. + /// The link parsers using to parse the path. + /// The path to be parsed. + /// The mathcing route values for the mathcing endpoint. + /// The endpoint or null if not found. + public static Endpoint? GetEndpointByPath(this EndpointDataSource endpointDatasource, LinkParser linkParser, PathString path, out RouteValueDictionary? routeValues) + { + routeValues = null; + + foreach (Endpoint endpoint in endpointDatasource.Endpoints) + { + routeValues = linkParser.ParsePathByEndpoint(endpoint, path); + + if (routeValues != null) + { + return endpoint; + } + } + + return null; + } +} diff --git a/src/Umbraco.Web.Common/Extensions/EndpointExtensions.cs b/src/Umbraco.Web.Common/Extensions/EndpointExtensions.cs new file mode 100644 index 0000000000..684c2e4c3f --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/EndpointExtensions.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Routing; + +namespace Umbraco.Cms.Web.Common.Extensions; + +/// +/// Extensions methods for Endpoint. +/// +public static class EndpointExtensions +{ + /// + /// Gets the controller action descriptor from the endpoint. + /// + /// The endpoint. + /// The controller action descriptor or null if not found. + public static ControllerActionDescriptor? GetControllerActionDescriptor(this Endpoint endpoint) + { + return endpoint.Metadata.GetMetadata(); + } + + /// + /// Gets the route name metadata from the endpoint. + /// + /// The endpoint. + /// The route name metadata or null if not found. + public static RouteNameMetadata? GetRouteNameMetadata(this Endpoint endpoint) + { + return endpoint.Metadata.GetMetadata(); + } + + /// + /// Gets the route name from the endpoint metadata. + /// + /// The endpoint. + /// The route name or null if not found. + public static string? GetRouteName(this Endpoint endpoint) + { + return endpoint.GetRouteNameMetadata()?.RouteName; + } +} diff --git a/src/Umbraco.Web.Common/Extensions/LinkParserExtensions.cs b/src/Umbraco.Web.Common/Extensions/LinkParserExtensions.cs new file mode 100644 index 0000000000..8cb9156103 --- /dev/null +++ b/src/Umbraco.Web.Common/Extensions/LinkParserExtensions.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Umbraco.Cms.Web.Common.Extensions; + +/// +/// Extension methods for LinkParser. +/// +public static class LinkParserExtensions +{ + /// + /// Parses the path using the specified endpoint and returns the route values if a the path matches the route pattern. + /// + /// The link parser. + /// The endpoint. + /// The path to parse. + /// The route values if the path matches or null. + public static RouteValueDictionary? ParsePathByEndpoint(this LinkParser linkParser, Endpoint endpoint, PathString path) + { + var name = endpoint.GetRouteName(); + + if (name != null) + { + return linkParser.ParsePathByEndpointName(name, path); + } + else + { + return null; + } + } +} diff --git a/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs b/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs index 7b1b9960e3..707dfe0b8d 100644 --- a/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs +++ b/src/Umbraco.Web.Common/Filters/UmbracoVirtualPageFilterAttribute.cs @@ -1,11 +1,9 @@ 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; @@ -21,23 +19,42 @@ public class UmbracoVirtualPageFilterAttribute : Attribute, IAsyncActionFilter /// public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { - Endpoint? endpoint = context.HttpContext.GetEndpoint(); + // Check if there's proxied ViewData (i.e. returned from a SurfaceController) + // We don't want to find the content and set route values again if this is from a surface controller + ProxyViewDataFeature? proxyViewDataFeature = context.HttpContext.Features.Get(); - // Check if there is any delegate in the metadata of the route, this - // will occur when using the ForUmbraco method during routing. - CustomRouteContentFinderDelegate? contentFinder = - endpoint?.Metadata.OfType().FirstOrDefault(); - - if (contentFinder != null) + if (proxyViewDataFeature != null) { - await SetUmbracoRouteValues(context, contentFinder.FindContent(context)); + if (context.Controller is Controller controller) + { + foreach (KeyValuePair kv in proxyViewDataFeature.ViewData) + { + controller.ViewData[kv.Key] = kv.Value; + } + } } else { - // Check if the controller is IVirtualPageController and then use that to FindContent - if (context.Controller is IVirtualPageController ctrl) + Endpoint? endpoint = context.HttpContext.GetEndpoint(); + + if (endpoint != null) { - await SetUmbracoRouteValues(context, ctrl.FindContent(context)); + IUmbracoVirtualPageRoute umbracoVirtualPageRoute = context.HttpContext.RequestServices.GetRequiredService(); + + IPublishedContent? publishedContent = umbracoVirtualPageRoute.FindContent(endpoint, context); + + if (publishedContent != null) + { + await umbracoVirtualPageRoute.SetRouteValues( + context.HttpContext, + publishedContent, + (ControllerActionDescriptor)context.ActionDescriptor); + } + else + { + // if there is no content then it should be a not found + context.Result = new NotFoundResult(); + } } } @@ -47,32 +64,4 @@ public class UmbracoVirtualPageFilterAttribute : Attribute, IAsyncActionFilter await next(); } } - - private async Task SetUmbracoRouteValues(ActionExecutingContext context, IPublishedContent? content) - { - if (content != null) - { - UriUtility uriUtility = context.HttpContext.RequestServices.GetRequiredService(); - - var originalRequestUrl = new Uri(context.HttpContext.Request.GetEncodedUrl()); - Uri cleanedUrl = uriUtility.UriToUmbraco(originalRequestUrl); - - IPublishedRouter router = context.HttpContext.RequestServices.GetRequiredService(); - - IPublishedRequestBuilder requestBuilder = await router.CreateRequestAsync(cleanedUrl); - requestBuilder.SetPublishedContent(content); - IPublishedRequest publishedRequest = requestBuilder.Build(); - - var routeValues = new UmbracoRouteValues( - publishedRequest, - (ControllerActionDescriptor)context.ActionDescriptor); - - context.HttpContext.Features.Set(routeValues); - } - else - { - // if there is no content then it should be a not found - context.Result = new NotFoundResult(); - } - } } diff --git a/src/Umbraco.Web.Common/Routing/IUmbracoVirtualPageRoute.cs b/src/Umbraco.Web.Common/Routing/IUmbracoVirtualPageRoute.cs new file mode 100644 index 0000000000..dc8453eb62 --- /dev/null +++ b/src/Umbraco.Web.Common/Routing/IUmbracoVirtualPageRoute.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Routing; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Routing; + +namespace Umbraco.Cms.Web.Common.Routing; + +/// +/// This is used to setup the virtual page route so the route values and content are set for virtual pages. +/// +public interface IUmbracoVirtualPageRoute +{ + /// + /// This sets up the virtual page route for the current request if a mtahcing endpoint is found. + /// + /// The HTTP context. + /// Nothing + Task SetupVirtualPageRoute(HttpContext httpContext); + + /// + /// Finds the content from the custom route finder delegate or the virtual page controller. + /// Note - This creates a dummay action executing context so the FindContent method of the + /// IVirtualPageController can be called (without changing the interface contract). + /// + /// The endpoint. + /// The HTTP context. + /// The route values. + /// The action descriptor. + /// The controller. + /// + IPublishedContent? FindContent( + Endpoint endpoint, + HttpContext httpContext, + RouteValueDictionary routeValues, + ControllerActionDescriptor controllerActionDescriptor, + object controller); + + /// + /// Finds the content from the custom route finder delegate or the virtual page controller. + /// + /// The endpoint. + /// The action executing context. + /// The published content if found or null. + IPublishedContent? FindContent(Endpoint endpoint, ActionExecutingContext actionExecutingContext); + + /// + /// Creates the published request for the published content. + /// + /// The HTTP context. + /// The published content. + /// The published request. + Task CreatePublishedRequest(HttpContext httpContext, IPublishedContent publishedContent); + + /// + /// Sets the route values for the published content and the controller action descriptor. + /// + /// The HTTP context. + /// The published content. + /// The controller action descriptor. + /// Nothing. + Task SetRouteValues(HttpContext httpContext, IPublishedContent publishedContent, ControllerActionDescriptor controllerActionDescriptor); +} diff --git a/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs b/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs new file mode 100644 index 0000000000..f0d0427438 --- /dev/null +++ b/src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs @@ -0,0 +1,178 @@ +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.AspNetCore.Routing; +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.Extensions; + +namespace Umbraco.Cms.Web.Common.Routing; + +/// +/// This is used to setup the virtual page route so the route values and content are set for virtual pages. +/// +public class UmbracoVirtualPageRoute : IUmbracoVirtualPageRoute +{ + private readonly EndpointDataSource _endpointDataSource; + private readonly LinkParser _linkParser; + private readonly UriUtility _uriUtility; + private readonly IPublishedRouter _publishedRouter; + + /// + /// Constructor. + /// + /// The endpoint data source. + /// The link parser. + /// The Uri utility. + /// The published router. + public UmbracoVirtualPageRoute( + EndpointDataSource endpointDataSource, + LinkParser linkParser, + UriUtility uriUtility, + IPublishedRouter publishedRouter) + { + _endpointDataSource = endpointDataSource; + _linkParser = linkParser; + _uriUtility = uriUtility; + _publishedRouter = publishedRouter; + } + + /// + /// This sets up the virtual page route for the current request if a mtahcing endpoint is found. + /// + /// The HTTP context. + /// Nothing + public async Task SetupVirtualPageRoute(HttpContext httpContext) + { + // Try and find an endpoint for the current path... + Endpoint? endpoint = _endpointDataSource.GetEndpointByPath(_linkParser, httpContext.Request.Path, out RouteValueDictionary? routeValues); + + if (endpoint != null && routeValues != null) + { + ControllerActionDescriptor? controllerActionDescriptor = endpoint.GetControllerActionDescriptor(); + + if (controllerActionDescriptor != null) + { + Type controllerType = controllerActionDescriptor.ControllerTypeInfo.AsType(); + + if (controllerType != null) + { + // Get the controller for the endpoint + var controller = httpContext.RequestServices.GetRequiredService(controllerType); + + // Try and find the content if this is a virtual page + IPublishedContent? publishedContent = FindContent( + endpoint, + httpContext, + routeValues, + controllerActionDescriptor, + controller); + + if (publishedContent != null) + { + // If we have content then set the route values + await SetRouteValues(httpContext, publishedContent, controllerActionDescriptor); + } + } + } + } + } + + /// + /// Finds the content from the custom route finder delegate or the virtual page controller. + /// Note - This creates a dummay action executing context so the FindContent method of the + /// IVirtualPageController can be called (without changing the interface contract). + /// + /// The endpoint. + /// The HTTP context. + /// The route values. + /// The action descriptor. + /// The controller. + /// + public IPublishedContent? FindContent( + Endpoint endpoint, + HttpContext httpContext, + RouteValueDictionary routeValues, + ControllerActionDescriptor controllerActionDescriptor, + object controller) + { + var actionExecutingContext = new ActionExecutingContext( + new ActionContext( + httpContext, + new RouteData(routeValues), + controllerActionDescriptor), + filters: new List(), + actionArguments: new Dictionary(), + controller: controller); + + return FindContent(endpoint, actionExecutingContext); + } + + /// + /// Finds the content from the custom route finder delegate or the virtual page controller. + /// + /// The endpoint. + /// The action executing context. + /// The published content if found or null. + public IPublishedContent? FindContent(Endpoint endpoint, ActionExecutingContext actionExecutingContext) + { + // Check if there is any delegate in the metadata of the route, this + // will occur when using the ForUmbraco method during routing. + CustomRouteContentFinderDelegate? contentFinder = + endpoint?.Metadata.OfType().FirstOrDefault(); + + if (contentFinder != null) + { + return contentFinder.FindContent(actionExecutingContext); + } + else + { + // Check if the controller is IVirtualPageController and then use that to FindContent + if (actionExecutingContext.Controller is IVirtualPageController virtualPageController) + { + return virtualPageController.FindContent(actionExecutingContext); + } + } + + return null; + } + + /// + /// Creates the published request for the published content. + /// + /// The HTTP context. + /// The published content. + /// The published request. + public async Task CreatePublishedRequest(HttpContext httpContext, IPublishedContent publishedContent) + { + var originalRequestUrl = new Uri(httpContext.Request.GetEncodedUrl()); + Uri cleanedUrl = _uriUtility.UriToUmbraco(originalRequestUrl); + + IPublishedRequestBuilder requestBuilder = await _publishedRouter.CreateRequestAsync(cleanedUrl); + requestBuilder.SetPublishedContent(publishedContent); + + return requestBuilder.Build(); + } + + /// + /// Sets the route values for the published content and the controller action descriptor. + /// + /// The HTTP context. + /// The published content. + /// The controller action descriptor. + /// Nothing. + public async Task SetRouteValues(HttpContext httpContext, IPublishedContent publishedContent, ControllerActionDescriptor controllerActionDescriptor) + { + IPublishedRequest publishedRequest = await CreatePublishedRequest(httpContext, publishedContent); + + var umbracoRouteValues = new UmbracoRouteValues( + publishedRequest, + controllerActionDescriptor); + + httpContext.Features.Set(umbracoRouteValues); + } +} diff --git a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs index 5260006768..1bd05a424d 100644 --- a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs @@ -1,9 +1,14 @@ +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.DependencyInjection; using Umbraco.Cms.Web.Common.Middleware; using Umbraco.Cms.Web.Common.Routing; @@ -40,9 +45,21 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddDataProtection(); builder.Services.AddAntiforgery(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton(x => new UmbracoRouteValueTransformer( + x.GetRequiredService>(), + x.GetRequiredService(), + x.GetRequiredService(), + x.GetRequiredService(), + x.GetRequiredService(), + x.GetRequiredService(), + x.GetRequiredService(), + x.GetRequiredService(), + x.GetRequiredService(), + x.GetRequiredService() + )); builder.Services.AddSingleton(); builder.Services.TryAddEnumerable(Singleton()); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs b/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs index d4394d2e2a..c1cc360500 100644 --- a/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs +++ b/src/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformer.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; @@ -13,6 +14,7 @@ using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Cms.Web.Common.Routing; using Umbraco.Cms.Web.Common.Security; using Umbraco.Cms.Web.Website.Controllers; @@ -46,8 +48,9 @@ public class UmbracoRouteValueTransformer : DynamicRouteValueTransformer private readonly IUmbracoRouteValuesFactory _routeValuesFactory; private readonly IRuntimeState _runtime; private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly IUmbracoVirtualPageRoute _umbracoVirtualPageRoute; - [Obsolete("Please use constructor that does not take IOptions, IHostingEnvironment & IEventAggregator instead")] + [Obsolete("Please use constructor that is not obsolete, instead of this. This will be removed in Umbraco 13.")] public UmbracoRouteValueTransformer( ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, @@ -61,10 +64,26 @@ public class UmbracoRouteValueTransformer : DynamicRouteValueTransformer IControllerActionSearcher controllerActionSearcher, IEventAggregator eventAggregator, IPublicAccessRequestHandler publicAccessRequestHandler) - : this(logger, umbracoContextAccessor, publishedRouter, runtime, routeValuesFactory, routableDocumentFilter, dataProtectionProvider, controllerActionSearcher, publicAccessRequestHandler) + : this(logger, umbracoContextAccessor, publishedRouter, runtime, routeValuesFactory, routableDocumentFilter, dataProtectionProvider, controllerActionSearcher, publicAccessRequestHandler, StaticServiceProvider.Instance.GetRequiredService()) { } + [Obsolete("Please use constructor that is not obsolete, instead of this. This will be removed in Umbraco 13.")] + public UmbracoRouteValueTransformer( + ILogger logger, + IUmbracoContextAccessor umbracoContextAccessor, + IPublishedRouter publishedRouter, + IRuntimeState runtime, + IUmbracoRouteValuesFactory routeValuesFactory, + IRoutableDocumentFilter routableDocumentFilter, + IDataProtectionProvider dataProtectionProvider, + IControllerActionSearcher controllerActionSearcher, + IPublicAccessRequestHandler publicAccessRequestHandler) + : this(logger, umbracoContextAccessor, publishedRouter, runtime, routeValuesFactory, routableDocumentFilter, dataProtectionProvider, controllerActionSearcher, publicAccessRequestHandler, StaticServiceProvider.Instance.GetRequiredService()) + { + } + + /// /// Initializes a new instance of the class. /// @@ -77,7 +96,8 @@ public class UmbracoRouteValueTransformer : DynamicRouteValueTransformer IRoutableDocumentFilter routableDocumentFilter, IDataProtectionProvider dataProtectionProvider, IControllerActionSearcher controllerActionSearcher, - IPublicAccessRequestHandler publicAccessRequestHandler) + IPublicAccessRequestHandler publicAccessRequestHandler, + IUmbracoVirtualPageRoute umbracoVirtualPageRoute) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _umbracoContextAccessor = @@ -90,6 +110,7 @@ public class UmbracoRouteValueTransformer : DynamicRouteValueTransformer _dataProtectionProvider = dataProtectionProvider; _controllerActionSearcher = controllerActionSearcher; _publicAccessRequestHandler = publicAccessRequestHandler; + _umbracoVirtualPageRoute = umbracoVirtualPageRoute; } /// @@ -147,6 +168,10 @@ public class UmbracoRouteValueTransformer : DynamicRouteValueTransformer PostedDataProxyInfo? postedInfo = GetFormInfo(httpContext, values); if (postedInfo != null) { + // Ensure the virtual page content and route values are setup when submitting to a surface controller + // If we don't do this, the virtual page controller never gets called after the surface controller completes + await _umbracoVirtualPageRoute.SetupVirtualPageRoute(httpContext); + return HandlePostedValues(postedInfo, httpContext); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs index 3f98ca4bcb..cd48e870fc 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Routing/UmbracoRouteValueTransformerTests.cs @@ -21,6 +21,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Tests.UnitTests.TestHelpers; using Umbraco.Cms.Web.Common.Controllers; +using Umbraco.Cms.Web.Common.Filters; using Umbraco.Cms.Web.Common.Routing; using Umbraco.Cms.Web.Website.Controllers; using Umbraco.Cms.Web.Website.Routing; @@ -53,20 +54,21 @@ public class UmbracoRouteValueTransformerTests x.RewriteForPublishedContentAccessAsync(It.IsAny(), It.IsAny())) .Returns((HttpContext ctx, UmbracoRouteValues routeVals) => Task.FromResult(routeVals)); + var umbracoVirtualPageRoute = new Mock(); + umbracoVirtualPageRoute.Setup(x => x.SetupVirtualPageRoute(It.IsAny())); + 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), Mock.Of(), Mock.Of(), - Mock.Of(), - publicAccessRequestHandler.Object); - + publicAccessRequestHandler.Object, + Mock.Of() + ); return transformer; }