Gets UmbracoPageResult working with ViewData and TempData

This commit is contained in:
Shannon
2021-02-04 14:51:55 +11:00
parent eda98aa41f
commit 6273c95a90
5 changed files with 58 additions and 134 deletions

View 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; }
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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; }
}
}
}

View File

@@ -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);
}
}
}