Gets UmbracoPageResult working with ViewData and TempData
This commit is contained in:
20
src/Umbraco.Web.Common/Controllers/ProxyViewDataFeature.cs
Normal file
20
src/Umbraco.Web.Common/Controllers/ProxyViewDataFeature.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace Umbraco.Web.Common.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// A request feature to allowing proxying viewdata from one controller to another
|
||||
/// </summary>
|
||||
public sealed class ProxyViewDataFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProxyViewDataFeature"/> class.
|
||||
/// </summary>
|
||||
public ProxyViewDataFeature(ViewDataDictionary viewData) => ViewData = viewData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ViewDataDictionary"/>
|
||||
/// </summary>
|
||||
public ViewDataDictionary ViewData { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
@@ -149,6 +150,20 @@ namespace Umbraco.Web.Common.Controllers
|
||||
break;
|
||||
case UmbracoRouteResult.Success:
|
||||
default:
|
||||
|
||||
// Check if there's a ProxyViewDataFeature in the request.
|
||||
// If there it is means that we are proxying/executing this controller
|
||||
// from another controller and we need to merge it's ViewData with this one
|
||||
// since this one will be empty.
|
||||
ProxyViewDataFeature saveViewData = HttpContext.Features.Get<ProxyViewDataFeature>();
|
||||
if (saveViewData != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> kv in saveViewData.ViewData)
|
||||
{
|
||||
ViewData[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// continue normally
|
||||
await next();
|
||||
break;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
/// <summary>
|
||||
/// Redirects to an Umbraco page by Id or Entity
|
||||
/// </summary>
|
||||
public class RedirectToUmbracoPageResult : IActionResult
|
||||
public class RedirectToUmbracoPageResult : IActionResult, IKeepTempDataResult
|
||||
{
|
||||
private IPublishedContent _publishedContent;
|
||||
private readonly QueryString _queryString;
|
||||
@@ -122,19 +122,15 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var httpContext = context.HttpContext;
|
||||
var ioHelper = httpContext.RequestServices.GetRequiredService<IIOHelper>();
|
||||
var destinationUrl = ioHelper.ResolveUrl(Url);
|
||||
HttpContext httpContext = context.HttpContext;
|
||||
IIOHelper ioHelper = httpContext.RequestServices.GetRequiredService<IIOHelper>();
|
||||
string destinationUrl = ioHelper.ResolveUrl(Url);
|
||||
|
||||
if (_queryString.HasValue)
|
||||
{
|
||||
destinationUrl += _queryString.ToUriComponent();
|
||||
}
|
||||
|
||||
var tempDataDictionaryFactory = context.HttpContext.RequestServices.GetRequiredService<ITempDataDictionaryFactory>();
|
||||
var tempData = tempDataDictionaryFactory.GetTempData(context.HttpContext);
|
||||
tempData?.Keep();
|
||||
|
||||
httpContext.Response.Redirect(destinationUrl);
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
using Umbraco.Web.Common.Routing;
|
||||
using Umbraco.Web.Website.Controllers;
|
||||
using Umbraco.Web.Website.Routing;
|
||||
@@ -25,14 +15,19 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
/// <summary>
|
||||
/// Used by posted forms to proxy the result to the page in which the current URL matches on
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This page does not redirect therefore it does not implement <see cref="IKeepTempDataResult"/> because TempData should
|
||||
/// only be used in situations when a redirect occurs. It is not good practice to use TempData when redirects do not occur
|
||||
/// so we'll be strict about it and not save it.
|
||||
/// </remarks>
|
||||
public class UmbracoPageResult : IActionResult
|
||||
{
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
|
||||
public UmbracoPageResult(IProfilingLogger profilingLogger)
|
||||
{
|
||||
_profilingLogger = profilingLogger;
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UmbracoPageResult"/> class.
|
||||
/// </summary>
|
||||
public UmbracoPageResult(IProfilingLogger profilingLogger) => _profilingLogger = profilingLogger;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task ExecuteResultAsync(ActionContext context)
|
||||
@@ -47,42 +42,15 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
context.RouteData.Values[UmbracoRouteValueTransformer.ControllerToken] = umbracoRouteValues.ControllerName;
|
||||
context.RouteData.Values[UmbracoRouteValueTransformer.ActionToken] = umbracoRouteValues.ActionName;
|
||||
|
||||
// Create a new context and excute the original controller
|
||||
|
||||
// TODO: We need to take into account temp data, view data, etc... all like what we used to do below
|
||||
// so that validation stuff gets carried accross
|
||||
|
||||
var renderActionContext = new ActionContext(context.HttpContext, context.RouteData, umbracoRouteValues.ControllerActionDescriptor);
|
||||
// Create a new context and excute the original controller...
|
||||
// Copy the action context - this also copies the ModelState
|
||||
var renderActionContext = new ActionContext(context)
|
||||
{
|
||||
ActionDescriptor = umbracoRouteValues.ControllerActionDescriptor
|
||||
};
|
||||
IActionInvokerFactory actionInvokerFactory = context.HttpContext.RequestServices.GetRequiredService<IActionInvokerFactory>();
|
||||
IActionInvoker actionInvoker = actionInvokerFactory.CreateInvoker(renderActionContext);
|
||||
await ExecuteControllerAction(actionInvoker);
|
||||
|
||||
//ResetRouteData(context.RouteData);
|
||||
//ValidateRouteData(context);
|
||||
|
||||
//IControllerFactory factory = context.HttpContext.RequestServices.GetRequiredService<IControllerFactory>();
|
||||
//Controller controller = null;
|
||||
|
||||
//if (!(context is ControllerContext controllerContext))
|
||||
//{
|
||||
// // TODO: Better to throw since this is not expected?
|
||||
// return Task.FromCanceled(CancellationToken.None);
|
||||
//}
|
||||
|
||||
//try
|
||||
//{
|
||||
// controller = CreateController(controllerContext, factory);
|
||||
|
||||
// CopyControllerData(controllerContext, controller);
|
||||
|
||||
// ExecuteControllerAction(controllerContext, controller);
|
||||
//}
|
||||
//finally
|
||||
//{
|
||||
// CleanupController(controllerContext, controller, factory);
|
||||
//}
|
||||
|
||||
//return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -95,83 +63,5 @@ namespace Umbraco.Web.Website.ActionResults
|
||||
await actionInvoker.InvokeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates action execution delegate from ActionExecutingContext
|
||||
/// </summary>
|
||||
private static ActionExecutionDelegate CreateActionExecutedDelegate(ActionExecutingContext context)
|
||||
{
|
||||
var actionExecutedContext = new ActionExecutedContext(context, context.Filters, context.Controller)
|
||||
{
|
||||
Result = context.Result,
|
||||
};
|
||||
return () => Task.FromResult(actionExecutedContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure ModelState, ViewData and TempData is copied across
|
||||
/// </summary>
|
||||
private static void CopyControllerData(ControllerContext context, Controller controller)
|
||||
{
|
||||
controller.ViewData.ModelState.Merge(context.ModelState);
|
||||
|
||||
foreach (KeyValuePair<string, object> d in controller.ViewData)
|
||||
{
|
||||
controller.ViewData[d.Key] = d.Value;
|
||||
}
|
||||
|
||||
// We cannot simply merge the temp data because during controller execution it will attempt to 'load' temp data
|
||||
// but since it has not been saved, there will be nothing to load and it will revert to nothing, so the trick is
|
||||
// to Save the state of the temp data first then it will automatically be picked up.
|
||||
// http://issues.umbraco.org/issue/U4-1339
|
||||
|
||||
var targetController = controller;
|
||||
var tempDataDictionaryFactory = context.HttpContext.RequestServices.GetRequiredService<ITempDataDictionaryFactory>();
|
||||
var tempData = tempDataDictionaryFactory.GetTempData(context.HttpContext);
|
||||
|
||||
targetController.TempData = tempData;
|
||||
targetController.TempData.Save();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a controller using the controller factory
|
||||
/// </summary>
|
||||
private static Controller CreateController(ControllerContext context, IControllerFactory factory)
|
||||
{
|
||||
if (!(factory.CreateController(context) is Controller controller))
|
||||
{
|
||||
throw new InvalidOperationException("Could not create controller with name " + context.ActionDescriptor.ControllerName + ".");
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up the controller by releasing it using the controller factory, and by disposing it.
|
||||
/// </summary>
|
||||
private static void CleanupController(ControllerContext context, Controller controller, IControllerFactory factory)
|
||||
{
|
||||
if (!(controller is null))
|
||||
{
|
||||
factory.ReleaseController(context, controller);
|
||||
}
|
||||
|
||||
controller?.DisposeIfDisposable();
|
||||
}
|
||||
|
||||
private class DummyView : IView
|
||||
{
|
||||
public DummyView(string path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public Task RenderAsync(ViewContext context)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,9 @@ namespace Umbraco.Web.Website.Controllers
|
||||
/// Returns the currently rendered Umbraco page
|
||||
/// </summary>
|
||||
protected UmbracoPageResult CurrentUmbracoPage()
|
||||
=> new UmbracoPageResult(ProfilingLogger);
|
||||
{
|
||||
HttpContext.Features.Set(new ProxyViewDataFeature(ViewData));
|
||||
return new UmbracoPageResult(ProfilingLogger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user