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 <mail@bergmania.dk>
This commit is contained in:
committed by
Bjarke Berg
parent
caed5bcb13
commit
f28b4c1279
@@ -0,0 +1,35 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions methods for EndpointDataSource.
|
||||
/// </summary>
|
||||
public static class EndpointDataSourceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an endpoint that matches the specified path.
|
||||
/// </summary>
|
||||
/// <param name="endpointDatasource">The end point data source.</param>
|
||||
/// <param name="linkParser">The link parsers using to parse the path.</param>
|
||||
/// <param name="path">The path to be parsed.</param>
|
||||
/// <param name="routeValues">The mathcing route values for the mathcing endpoint.</param>
|
||||
/// <returns>The endpoint or null if not found.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
41
src/Umbraco.Web.Common/Extensions/EndpointExtensions.cs
Normal file
41
src/Umbraco.Web.Common/Extensions/EndpointExtensions.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions methods for Endpoint.
|
||||
/// </summary>
|
||||
public static class EndpointExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the controller action descriptor from the endpoint.
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <returns>The controller action descriptor or null if not found.</returns>
|
||||
public static ControllerActionDescriptor? GetControllerActionDescriptor(this Endpoint endpoint)
|
||||
{
|
||||
return endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route name metadata from the endpoint.
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <returns>The route name metadata or null if not found.</returns>
|
||||
public static RouteNameMetadata? GetRouteNameMetadata(this Endpoint endpoint)
|
||||
{
|
||||
return endpoint.Metadata.GetMetadata<RouteNameMetadata>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route name from the endpoint metadata.
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <returns>The route name or null if not found.</returns>
|
||||
public static string? GetRouteName(this Endpoint endpoint)
|
||||
{
|
||||
return endpoint.GetRouteNameMetadata()?.RouteName;
|
||||
}
|
||||
}
|
||||
31
src/Umbraco.Web.Common/Extensions/LinkParserExtensions.cs
Normal file
31
src/Umbraco.Web.Common/Extensions/LinkParserExtensions.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for LinkParser.
|
||||
/// </summary>
|
||||
public static class LinkParserExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses the path using the specified endpoint and returns the route values if a the path matches the route pattern.
|
||||
/// </summary>
|
||||
/// <param name="linkParser">The link parser.</param>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <param name="path">The path to parse.</param>
|
||||
/// <returns>The route values if the path matches or null.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <inheritdoc />
|
||||
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<ProxyViewDataFeature>();
|
||||
|
||||
// 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<CustomRouteContentFinderDelegate>().FirstOrDefault();
|
||||
|
||||
if (contentFinder != null)
|
||||
if (proxyViewDataFeature != null)
|
||||
{
|
||||
await SetUmbracoRouteValues(context, contentFinder.FindContent(context));
|
||||
if (context.Controller is Controller controller)
|
||||
{
|
||||
foreach (KeyValuePair<string, object?> 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<IUmbracoVirtualPageRoute>();
|
||||
|
||||
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<UriUtility>();
|
||||
|
||||
var originalRequestUrl = new Uri(context.HttpContext.Request.GetEncodedUrl());
|
||||
Uri cleanedUrl = uriUtility.UriToUmbraco(originalRequestUrl);
|
||||
|
||||
IPublishedRouter router = context.HttpContext.RequestServices.GetRequiredService<IPublishedRouter>();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
64
src/Umbraco.Web.Common/Routing/IUmbracoVirtualPageRoute.cs
Normal file
64
src/Umbraco.Web.Common/Routing/IUmbracoVirtualPageRoute.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This is used to setup the virtual page route so the route values and content are set for virtual pages.
|
||||
/// </summary>
|
||||
public interface IUmbracoVirtualPageRoute
|
||||
{
|
||||
/// <summary>
|
||||
/// This sets up the virtual page route for the current request if a mtahcing endpoint is found.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HTTP context.</param>
|
||||
/// <returns>Nothing</returns>
|
||||
Task SetupVirtualPageRoute(HttpContext httpContext);
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <param name="httpContext">The HTTP context.</param>
|
||||
/// <param name="routeValues">The route values.</param>
|
||||
/// <param name="controllerActionDescriptor">The action descriptor.</param>
|
||||
/// <param name="controller">The controller.</param>
|
||||
/// <returns></returns>
|
||||
IPublishedContent? FindContent(
|
||||
Endpoint endpoint,
|
||||
HttpContext httpContext,
|
||||
RouteValueDictionary routeValues,
|
||||
ControllerActionDescriptor controllerActionDescriptor,
|
||||
object controller);
|
||||
|
||||
/// <summary>
|
||||
/// Finds the content from the custom route finder delegate or the virtual page controller.
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <param name="actionExecutingContext">The action executing context.</param>
|
||||
/// <returns>The published content if found or null.</returns>
|
||||
IPublishedContent? FindContent(Endpoint endpoint, ActionExecutingContext actionExecutingContext);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the published request for the published content.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HTTP context.</param>
|
||||
/// <param name="publishedContent">The published content.</param>
|
||||
/// <returns>The published request.</returns>
|
||||
Task<IPublishedRequest> CreatePublishedRequest(HttpContext httpContext, IPublishedContent publishedContent);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the route values for the published content and the controller action descriptor.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HTTP context.</param>
|
||||
/// <param name="publishedContent">The published content.</param>
|
||||
/// <param name="controllerActionDescriptor">The controller action descriptor.</param>
|
||||
/// <returns>Nothing.</returns>
|
||||
Task SetRouteValues(HttpContext httpContext, IPublishedContent publishedContent, ControllerActionDescriptor controllerActionDescriptor);
|
||||
}
|
||||
178
src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs
Normal file
178
src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This is used to setup the virtual page route so the route values and content are set for virtual pages.
|
||||
/// </summary>
|
||||
public class UmbracoVirtualPageRoute : IUmbracoVirtualPageRoute
|
||||
{
|
||||
private readonly EndpointDataSource _endpointDataSource;
|
||||
private readonly LinkParser _linkParser;
|
||||
private readonly UriUtility _uriUtility;
|
||||
private readonly IPublishedRouter _publishedRouter;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="endpointDataSource">The endpoint data source.</param>
|
||||
/// <param name="linkParser">The link parser.</param>
|
||||
/// <param name="uriUtility">The Uri utility.</param>
|
||||
/// <param name="publishedRouter">The published router.</param>
|
||||
public UmbracoVirtualPageRoute(
|
||||
EndpointDataSource endpointDataSource,
|
||||
LinkParser linkParser,
|
||||
UriUtility uriUtility,
|
||||
IPublishedRouter publishedRouter)
|
||||
{
|
||||
_endpointDataSource = endpointDataSource;
|
||||
_linkParser = linkParser;
|
||||
_uriUtility = uriUtility;
|
||||
_publishedRouter = publishedRouter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This sets up the virtual page route for the current request if a mtahcing endpoint is found.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HTTP context.</param>
|
||||
/// <returns>Nothing</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <param name="httpContext">The HTTP context.</param>
|
||||
/// <param name="routeValues">The route values.</param>
|
||||
/// <param name="controllerActionDescriptor">The action descriptor.</param>
|
||||
/// <param name="controller">The controller.</param>
|
||||
/// <returns></returns>
|
||||
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<IFilterMetadata>(),
|
||||
actionArguments: new Dictionary<string, object?>(),
|
||||
controller: controller);
|
||||
|
||||
return FindContent(endpoint, actionExecutingContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the content from the custom route finder delegate or the virtual page controller.
|
||||
/// </summary>
|
||||
/// <param name="endpoint">The endpoint.</param>
|
||||
/// <param name="actionExecutingContext">The action executing context.</param>
|
||||
/// <returns>The published content if found or null.</returns>
|
||||
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<CustomRouteContentFinderDelegate>().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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the published request for the published content.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HTTP context.</param>
|
||||
/// <param name="publishedContent">The published content.</param>
|
||||
/// <returns>The published request.</returns>
|
||||
public async Task<IPublishedRequest> 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the route values for the published content and the controller action descriptor.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The HTTP context.</param>
|
||||
/// <param name="publishedContent">The published content.</param>
|
||||
/// <param name="controllerActionDescriptor">The controller action descriptor.</param>
|
||||
/// <returns>Nothing.</returns>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<UmbracoRouteValueTransformer>();
|
||||
builder.Services.AddSingleton<UmbracoRouteValueTransformer>(x => new UmbracoRouteValueTransformer(
|
||||
x.GetRequiredService<ILogger<UmbracoRouteValueTransformer>>(),
|
||||
x.GetRequiredService<IUmbracoContextAccessor>(),
|
||||
x.GetRequiredService<IPublishedRouter>(),
|
||||
x.GetRequiredService<IRuntimeState>(),
|
||||
x.GetRequiredService<IUmbracoRouteValuesFactory>(),
|
||||
x.GetRequiredService<IRoutableDocumentFilter>(),
|
||||
x.GetRequiredService<IDataProtectionProvider>(),
|
||||
x.GetRequiredService<IControllerActionSearcher>(),
|
||||
x.GetRequiredService<IPublicAccessRequestHandler>(),
|
||||
x.GetRequiredService<IUmbracoVirtualPageRoute>()
|
||||
));
|
||||
builder.Services.AddSingleton<IControllerActionSearcher, ControllerActionSearcher>();
|
||||
builder.Services.TryAddEnumerable(Singleton<MatcherPolicy, NotFoundSelectorPolicy>());
|
||||
builder.Services.AddSingleton<IUmbracoVirtualPageRoute, UmbracoVirtualPageRoute>();
|
||||
builder.Services.AddSingleton<IUmbracoRouteValuesFactory, UmbracoRouteValuesFactory>();
|
||||
builder.Services.AddSingleton<IRoutableDocumentFilter, RoutableDocumentFilter>();
|
||||
|
||||
|
||||
@@ -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<GlobalSettings>, IHostingEnvironment & IEventAggregator instead")]
|
||||
[Obsolete("Please use constructor that is not obsolete, instead of this. This will be removed in Umbraco 13.")]
|
||||
public UmbracoRouteValueTransformer(
|
||||
ILogger<UmbracoRouteValueTransformer> 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<IUmbracoVirtualPageRoute>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Please use constructor that is not obsolete, instead of this. This will be removed in Umbraco 13.")]
|
||||
public UmbracoRouteValueTransformer(
|
||||
ILogger<UmbracoRouteValueTransformer> 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<IUmbracoVirtualPageRoute>())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoRouteValueTransformer" /> class.
|
||||
/// </summary>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<HttpContext>(), It.IsAny<UmbracoRouteValues>()))
|
||||
.Returns((HttpContext ctx, UmbracoRouteValues routeVals) => Task.FromResult(routeVals));
|
||||
|
||||
var umbracoVirtualPageRoute = new Mock<IUmbracoVirtualPageRoute>();
|
||||
umbracoVirtualPageRoute.Setup(x => x.SetupVirtualPageRoute(It.IsAny<HttpContext>()));
|
||||
|
||||
var transformer = new UmbracoRouteValueTransformer(
|
||||
new NullLogger<UmbracoRouteValueTransformer>(),
|
||||
ctx,
|
||||
router ?? Mock.Of<IPublishedRouter>(),
|
||||
GetGlobalSettings(),
|
||||
TestHelper.GetHostingEnvironment(),
|
||||
state,
|
||||
routeValuesFactory ?? Mock.Of<IUmbracoRouteValuesFactory>(),
|
||||
filter ?? Mock.Of<IRoutableDocumentFilter>(x => x.IsDocumentRequest(It.IsAny<string>()) == true),
|
||||
Mock.Of<IDataProtectionProvider>(),
|
||||
Mock.Of<IControllerActionSearcher>(),
|
||||
Mock.Of<IEventAggregator>(),
|
||||
publicAccessRequestHandler.Object);
|
||||
|
||||
publicAccessRequestHandler.Object,
|
||||
Mock.Of<IUmbracoVirtualPageRoute>()
|
||||
);
|
||||
return transformer;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user