More viewengine migration. Including wrapping in profiler etcs

Signed-off-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Bjarke Berg
2020-11-03 14:14:01 +01:00
parent 3faf0cb9ce
commit aefb596b4b
16 changed files with 225 additions and 126 deletions

View File

@@ -20,7 +20,7 @@
public const string AppCode = "~/App_Code";
public const string AppPlugins = "~/App_Plugins";
public const string AppPlugins = "/App_Plugins";
public const string MvcViews = "~/Views";

View File

@@ -0,0 +1,75 @@
using Microsoft.AspNetCore.Mvc;
using Moq;
using NUnit.Framework;
using Umbraco.Core.Logging;
using Umbraco.Web.Website.ViewEngines;
namespace Umbraco.Tests.Runtimes
{
[TestFixture]
public class ProfilingViewEngineWrapperMvcViewOptionsSetupTests
{
private ProfilingViewEngineWrapperMvcViewOptionsSetup Sut =>
new ProfilingViewEngineWrapperMvcViewOptionsSetup(Mock.Of<IProfiler>());
[Test]
public void WrapViewEngines_HasEngines_WrapsAll()
{
var options = new MvcViewOptions()
{
ViewEngines =
{
Mock.Of<IRenderViewEngine>(),
Mock.Of<IPluginViewEngine>(),
}
};
Sut.Configure(options);
Assert.That(options.ViewEngines.Count, Is.EqualTo(2));
Assert.That(options.ViewEngines[0], Is.InstanceOf<ProfilingViewEngine>());
Assert.That(options.ViewEngines[1], Is.InstanceOf<ProfilingViewEngine>());
}
[Test]
public void WrapViewEngines_HasEngines_KeepsSortOrder()
{
var options = new MvcViewOptions()
{
ViewEngines =
{
Mock.Of<IRenderViewEngine>(),
Mock.Of<IPluginViewEngine>(),
}
};
Sut.Configure(options);
Assert.That(options.ViewEngines.Count, Is.EqualTo(2));
Assert.That(((ProfilingViewEngine)options.ViewEngines[0]).Inner, Is.InstanceOf<IRenderViewEngine>());
Assert.That(((ProfilingViewEngine)options.ViewEngines[1]).Inner, Is.InstanceOf<IPluginViewEngine>());
}
[Test]
public void WrapViewEngines_HasProfiledEngine_AddsSameInstance()
{
var profiledEngine = new ProfilingViewEngine(Mock.Of<IPluginViewEngine>(), Mock.Of<IProfiler>());
var options = new MvcViewOptions()
{
ViewEngines =
{
profiledEngine
}
};
Sut.Configure(options);
Assert.That(options.ViewEngines[0], Is.SameAs(profiledEngine));
}
[Test]
public void WrapViewEngines_CollectionIsNull_DoesNotThrow()
{
Assert.DoesNotThrow(() => Sut.Configure(new MvcViewOptions()));
}
}
}

View File

@@ -1,66 +0,0 @@
using System.Collections.Generic;
using System.Web.Mvc;
using NUnit.Framework;
using NUnit.Framework.Internal;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web.Mvc;
using Umbraco.Web.Runtime;
namespace Umbraco.Tests.Runtimes
{
[TestFixture]
public class WebRuntimeComponentTests
{
[Test]
public void WrapViewEngines_HasEngines_WrapsAll()
{
IList<IViewEngine> engines = new List<IViewEngine>
{
new RenderViewEngine(TestHelper.GetHostingEnvironment()),
new PluginViewEngine()
};
WebInitialComponent.WrapViewEngines(engines);
Assert.That(engines.Count, Is.EqualTo(2));
Assert.That(engines[0], Is.InstanceOf<ProfilingViewEngine>());
Assert.That(engines[1], Is.InstanceOf<ProfilingViewEngine>());
}
[Test]
public void WrapViewEngines_HasEngines_KeepsSortOrder()
{
IList<IViewEngine> engines = new List<IViewEngine>
{
new RenderViewEngine(TestHelper.GetHostingEnvironment()),
new PluginViewEngine()
};
WebInitialComponent.WrapViewEngines(engines);
Assert.That(engines.Count, Is.EqualTo(2));
Assert.That(((ProfilingViewEngine)engines[0]).Inner, Is.InstanceOf<RenderViewEngine>());
Assert.That(((ProfilingViewEngine)engines[1]).Inner, Is.InstanceOf<PluginViewEngine>());
}
[Test]
public void WrapViewEngines_HasProfiledEngine_AddsSameInstance()
{
var profiledEngine = new ProfilingViewEngine(new PluginViewEngine());
IList<IViewEngine> engines = new List<IViewEngine>
{
profiledEngine
};
WebInitialComponent.WrapViewEngines(engines);
Assert.That(engines[0], Is.SameAs(profiledEngine));
}
[Test]
public void WrapViewEngines_CollectionIsNull_DoesNotThrow()
{
Assert.DoesNotThrow(() => WebInitialComponent.WrapViewEngines(null));
}
}
}

View File

@@ -217,7 +217,6 @@
<Compile Include="Testing\TestingTests\MockTests.cs" />
<Compile Include="Web\Mvc\SurfaceControllerTests.cs" />
<Compile Include="Runtimes\CoreRuntimeTests.cs" />
<Compile Include="Runtimes\WebRuntimeComponentTests.cs" />
<Compile Include="Web\Mvc\MergeParentContextViewDataAttributeTests.cs" />
<Compile Include="Web\Mvc\ViewDataDictionaryExtensionTests.cs" />
<Compile Include="Persistence\Querying\ContentTypeSqlMappingTests.cs" />

View File

@@ -38,7 +38,7 @@
"ConvertUrlsToAscii": "try"
},
"RuntimeMinification": {
"dataFolder": "App_Data/TEMP/Smidge",
"dataFolder": "App_Data/TEMP/Smidge",
"version": "1"
},
"Security": {

View File

@@ -11,19 +11,9 @@ namespace Umbraco.Extensions
/// <param name="actionContext">The action context.</param>
/// <param name="dataTokenName">The name of the data token.</param>
/// <returns>The data token, or null.</returns>
/// MIGRAGED TO NETCORE AS EXTENSION ON HTTPCONTEXT
internal static object GetDataTokenInViewContextHierarchy(this ActionContext actionContext, string dataTokenName)
{
var controllerActionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
while (!(controllerActionDescriptor is null))
{
object token;
if (actionContext.RouteData.DataTokens.TryGetValue(dataTokenName, out token))
return token;
controllerActionDescriptor = controllerActionDescriptor.ParentActionViewContext;
}
return null;
return actionContext.RouteData.DataTokens.TryGetValue(dataTokenName, out var token) ? token : null;
}
}
}

View File

@@ -1,5 +1,11 @@
using Microsoft.AspNetCore.Mvc.Controllers;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Web.Website.ViewEngines;
namespace Umbraco.Extensions
{
@@ -8,6 +14,18 @@ namespace Umbraco.Extensions
public static void AddUmbracoWebsite(this IServiceCollection services)
{
services.AddSingleton<IControllerActivator, ServiceBasedControllerActivator>();
// Set the render & plugin view engines (Super complicated, but this allows us to use the IServiceCollection
// to inject dependencies into the viewEngines)
services.AddTransient<IConfigureOptions<MvcViewOptions>, RenderMvcViewOptionsSetup>();
services.AddSingleton<IRenderViewEngine, RenderViewEngine>();
services.AddTransient<IConfigureOptions<MvcViewOptions>, PluginMvcViewOptionsSetup>();
services.AddSingleton<IPluginViewEngine, PluginViewEngine>();
// Wraps all existing view engines in a ProfilerViewEngine
services.AddTransient<IConfigureOptions<MvcViewOptions>, ProfilingViewEngineWrapperMvcViewOptionsSetup>();
}
}
}

View File

@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Mvc.Razor;
namespace Umbraco.Web.Website.ViewEngines
{
public interface IPluginViewEngine : IRazorViewEngine
{
}
}

View File

@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Mvc.Razor;
namespace Umbraco.Web.Website.ViewEngines
{
public interface IRenderViewEngine : IRazorViewEngine
{
}
}

View File

@@ -0,0 +1,26 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace Umbraco.Web.Website.ViewEngines
{
public class PluginMvcViewOptionsSetup : IConfigureOptions<MvcViewOptions>
{
private readonly IPluginViewEngine _pluginViewEngine;
public PluginMvcViewOptionsSetup(IPluginViewEngine pluginViewEngine)
{
_pluginViewEngine = pluginViewEngine ?? throw new ArgumentNullException(nameof(pluginViewEngine));
}
public void Configure(MvcViewOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
options.ViewEngines.Add(_pluginViewEngine);
}
}
}

View File

@@ -9,7 +9,7 @@ namespace Umbraco.Web.Website.ViewEngines
/// <summary>
/// A view engine to look into the App_Plugins folder for views for packaged controllers
/// </summary>
public class PluginViewEngine : RazorViewEngine
public class PluginViewEngine : RazorViewEngine, IPluginViewEngine
{
public PluginViewEngine(
IRazorPageFactoryProvider pageFactory,

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.Extensions.Options;
using Umbraco.Core.Logging;
namespace Umbraco.Web.Website.ViewEngines
{
public class ProfilingViewEngineWrapperMvcViewOptionsSetup : IConfigureOptions<MvcViewOptions>
{
private readonly IProfiler _profiler;
public ProfilingViewEngineWrapperMvcViewOptionsSetup(IProfiler profiler)
{
_profiler = profiler ?? throw new ArgumentNullException(nameof(profiler));
}
public void Configure(MvcViewOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
WrapViewEngines(options.ViewEngines);
}
private void WrapViewEngines(IList<IViewEngine> viewEngines)
{
if (viewEngines == null || viewEngines.Count == 0) return;
var originalEngines = viewEngines.ToList();
viewEngines.Clear();
foreach (var engine in originalEngines)
{
var wrappedEngine = engine is ProfilingViewEngine ? engine : new ProfilingViewEngine(engine, _profiler);
viewEngines.Add(wrappedEngine);
}
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace Umbraco.Web.Website.ViewEngines
{
public class RenderMvcViewOptionsSetup : IConfigureOptions<MvcViewOptions>
{
private readonly IRenderViewEngine _renderViewEngine;
public RenderMvcViewOptionsSetup(IRenderViewEngine renderViewEngine)
{
_renderViewEngine = renderViewEngine ?? throw new ArgumentNullException(nameof(renderViewEngine));
}
public void Configure(MvcViewOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
options.ViewEngines.Add(_renderViewEngine);
}
}
}

View File

@@ -20,7 +20,7 @@ namespace Umbraco.Web.Website.ViewEngines
/// A view engine to look into the template location specified in the config for the front-end/Rendering part of the cms,
/// this includes paths to render partial macros and media item templates.
/// </summary>
public class RenderViewEngine : RazorViewEngine
public class RenderViewEngine : RazorViewEngine, IRenderViewEngine
{
public RenderViewEngine(
@@ -46,50 +46,22 @@ namespace Umbraco.Web.Website.ViewEngines
"/MacroPartials/{0}.cshtml",
"/{0}.cshtml"
},
AreaViewLocationFormats =
{
"/Partials/{0}.cshtml",
"/MacroPartials/{0}.cshtml",
"/{0}.cshtml"
}
});
}
public new ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage)
{
return ShouldFindView(context, viewName, isMainPage)
return ShouldFindView(context, isMainPage)
? base.FindView(context, viewName, isMainPage)
: ViewEngineResult.NotFound(viewName, Array.Empty<string>());
}
// /// <summary>
// /// Constructor
// /// </summary>
// public RenderViewEngine(IHostingEnvironment hostingEnvironment)
// {
// _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
//
// const string templateFolder = Constants.ViewLocation;
//
// // the Render view engine doesn't support Area's so make those blank
// ViewLocationFormats = _supplementedViewLocations.Select(x => templateFolder + x).ToArray();
// PartialViewLocationFormats = _supplementedPartialViewLocations.Select(x => templateFolder + x).ToArray();
//
// AreaPartialViewLocationFormats = Array.Empty<string>();
// AreaViewLocationFormats = Array.Empty<string>();
//
// EnsureFoldersAndFiles();
// }
// public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
// {
// return ShouldFindView(controllerContext, false)
// ? base.FindView(controllerContext, viewName, masterName, useCache)
// : new ViewEngineResult(new string[] { });
// }
//
// public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
// {
// return ShouldFindView(controllerContext, true)
// ? base.FindPartialView(controllerContext, partialViewName, useCache)
// : new ViewEngineResult(new string[] { });
// }
/// <summary>
/// Determines if the view should be found, this is used for view lookup performance and also to ensure
/// less overlap with other user's view engines. This will return true if the Umbraco back office is rendering
@@ -98,14 +70,14 @@ namespace Umbraco.Web.Website.ViewEngines
/// <param name="controllerContext"></param>
/// <param name="isPartial"></param>
/// <returns></returns>
private static bool ShouldFindView(ActionContext context, string viewName, bool isMainPage)
private static bool ShouldFindView(ActionContext context, bool isMainPage)
{
var umbracoToken = context.GetDataTokenInViewContextHierarchy(Core.Constants.Web.UmbracoDataToken);
context.ActionDescriptor.
//In v8, this was testing recursively into if it was a child action, but child action do not exist anymore,
//And my best guess is that it
context.RouteData.DataTokens.TryGetValue(Core.Constants.Web.UmbracoDataToken, out var umbracoToken);
// first check if we're rendering a partial view for the back office, or surface controller, etc...
// anything that is not ContentModel as this should only pertain to Umbraco views.
if (isPartial && !(umbracoToken is ContentModel))
if (!isMainPage && !(umbracoToken is ContentModel))
return true;
// only find views if we're rendering the umbraco front end

View File

@@ -22,7 +22,7 @@ namespace Umbraco.Web.Mvc
/// <param name="controllerContext">The controller context.</param>
/// <param name="dataTokenName">The name of the data token.</param>
/// <returns>The data token, or null.</returns>
/// MIGRAGED TO NETCORE AS EXTENSION ON ActionContext
/// MIGRATED TO NETCORE AS EXTENSION ON ActionContext
internal static object GetDataTokenInViewContextHierarchy(this ControllerContext controllerContext, string dataTokenName)
{
var context = controllerContext;

View File

@@ -93,8 +93,8 @@ namespace Umbraco.Web.Runtime
// ControllerBuilder.Current.SetControllerFactory(controllerFactory);
// set the render & plugin view engines
ViewEngines.Engines.Add(new RenderViewEngine(_hostingEnvironment));
ViewEngines.Engines.Add(new PluginViewEngine());
// ViewEngines.Engines.Add(new RenderViewEngine(_hostingEnvironment));
// ViewEngines.Engines.Add(new PluginViewEngine());
////add the profiling action filter
//GlobalFilters.Filters.Add(new ProfilingActionFilter());