parent
3536cf5aad
commit
d1813a7cd7
|
|
@ -8,6 +8,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
|||
{
|
||||
public class CompiledViewDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The normalized application relative path of the view.
|
||||
/// </summary>
|
||||
public string RelativePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
var pageFactory = Expression
|
||||
.Lambda<Func<IRazorPage>>(objectInitializeExpression)
|
||||
.Compile();
|
||||
return new RazorPageFactoryResult(pageFactory, viewDescriptor.ExpirationTokens, viewDescriptor.IsPrecompiled);
|
||||
return new RazorPageFactoryResult(viewDescriptor, pageFactory);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new RazorPageFactoryResult(viewDescriptor.ExpirationTokens);
|
||||
return new RazorPageFactoryResult(viewDescriptor, razorPageFactory: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor
|
||||
{
|
||||
|
|
@ -12,61 +11,18 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
|||
/// </summary>
|
||||
public struct RazorPageFactoryResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
|
||||
/// specified <paramref name="expirationTokens"/>.
|
||||
/// </summary>
|
||||
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
|
||||
public RazorPageFactoryResult(IList<IChangeToken> expirationTokens)
|
||||
{
|
||||
if (expirationTokens == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(expirationTokens));
|
||||
}
|
||||
|
||||
ExpirationTokens = expirationTokens;
|
||||
RazorPageFactory = null;
|
||||
IsPrecompiled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
|
||||
/// specified <see cref="IRazorPage"/> factory.
|
||||
/// </summary>
|
||||
/// <param name="razorPageFactory">The <see cref="IRazorPage"/> factory.</param>
|
||||
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
|
||||
/// <param name="viewDescriptor">The <see cref="CompiledViewDescriptor"/>.</param>
|
||||
public RazorPageFactoryResult(
|
||||
Func<IRazorPage> razorPageFactory,
|
||||
IList<IChangeToken> expirationTokens)
|
||||
: this(razorPageFactory, expirationTokens, isPrecompiled: false)
|
||||
CompiledViewDescriptor viewDescriptor,
|
||||
Func<IRazorPage> razorPageFactory)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
|
||||
/// specified <see cref="IRazorPage"/> factory.
|
||||
/// </summary>
|
||||
/// <param name="razorPageFactory">The <see cref="IRazorPage"/> factory.</param>
|
||||
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
|
||||
/// <param name="isPrecompiled"><c>true</c> if the view is precompiled, <c>false</c> otherwise.</param>
|
||||
public RazorPageFactoryResult(
|
||||
Func<IRazorPage> razorPageFactory,
|
||||
IList<IChangeToken> expirationTokens,
|
||||
bool isPrecompiled)
|
||||
{
|
||||
if (razorPageFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(razorPageFactory));
|
||||
}
|
||||
|
||||
if (expirationTokens == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(expirationTokens));
|
||||
}
|
||||
|
||||
ViewDescriptor = viewDescriptor ?? throw new ArgumentNullException(nameof(viewDescriptor));
|
||||
RazorPageFactory = razorPageFactory;
|
||||
ExpirationTokens = expirationTokens;
|
||||
IsPrecompiled = isPrecompiled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -76,19 +32,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
|||
public Func<IRazorPage> RazorPageFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// One or more <see cref="IChangeToken"/>s associated with this instance of
|
||||
/// <see cref="RazorPageFactoryResult"/>.
|
||||
/// Gets the <see cref="CompiledViewDescriptor"/>.
|
||||
/// </summary>
|
||||
public IList<IChangeToken> ExpirationTokens { get; }
|
||||
public CompiledViewDescriptor ViewDescriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that determines if the page was successfully located.
|
||||
/// </summary>
|
||||
public bool Success => RazorPageFactory != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that determines if the view is precompiled.
|
||||
/// </summary>
|
||||
public bool IsPrecompiled { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -396,11 +396,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
|||
bool isMainPage)
|
||||
{
|
||||
var factoryResult = _pageFactory.CreateFactory(relativePath);
|
||||
if (factoryResult.ExpirationTokens != null)
|
||||
var viewDescriptor = factoryResult.ViewDescriptor;
|
||||
if (viewDescriptor?.ExpirationTokens != null)
|
||||
{
|
||||
for (var i = 0; i < factoryResult.ExpirationTokens.Count; i++)
|
||||
for (var i = 0; i < viewDescriptor.ExpirationTokens.Count; i++)
|
||||
{
|
||||
expirationTokens.Add(factoryResult.ExpirationTokens[i]);
|
||||
expirationTokens.Add(viewDescriptor.ExpirationTokens[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -408,9 +409,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
|||
{
|
||||
// Only need to lookup _ViewStarts for the main page.
|
||||
var viewStartPages = isMainPage ?
|
||||
GetViewStartPages(relativePath, expirationTokens) :
|
||||
GetViewStartPages(viewDescriptor.RelativePath, expirationTokens) :
|
||||
Array.Empty<ViewLocationCacheItem>();
|
||||
if (factoryResult.IsPrecompiled)
|
||||
if (viewDescriptor.IsPrecompiled)
|
||||
{
|
||||
_logger.PrecompiledViewFound(relativePath);
|
||||
}
|
||||
|
|
@ -427,17 +428,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
|||
string path,
|
||||
HashSet<IChangeToken> expirationTokens)
|
||||
{
|
||||
var applicationRelativePath = MakePathApplicationRelative(path);
|
||||
var viewStartPages = new List<ViewLocationCacheItem>();
|
||||
|
||||
foreach (var viewStartProjectItem in _razorProject.FindHierarchicalItems(applicationRelativePath, ViewStartFileName))
|
||||
foreach (var viewStartProjectItem in _razorProject.FindHierarchicalItems(path, ViewStartFileName))
|
||||
{
|
||||
var result = _pageFactory.CreateFactory(viewStartProjectItem.Path);
|
||||
if (result.ExpirationTokens != null)
|
||||
var viewDescriptor = result.ViewDescriptor;
|
||||
if (viewDescriptor?.ExpirationTokens != null)
|
||||
{
|
||||
for (var i = 0; i < result.ExpirationTokens.Count; i++)
|
||||
for (var i = 0; i < viewDescriptor.ExpirationTokens.Count; i++)
|
||||
{
|
||||
expirationTokens.Add(result.ExpirationTokens[i]);
|
||||
expirationTokens.Add(viewDescriptor.ExpirationTokens[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -479,22 +480,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
|||
return name[0] == '~' || name[0] == '/';
|
||||
}
|
||||
|
||||
private string MakePathApplicationRelative(string path)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(path));
|
||||
if (path[0] == '~')
|
||||
{
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
if (path[0] != '/')
|
||||
{
|
||||
path = '/' + path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private static bool IsRelativePath(string name)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(name));
|
||||
|
|
|
|||
|
|
@ -476,5 +476,21 @@ Partial that does not specify Layout
|
|||
ignoreLineEndingDifferences: true);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewEngine_NormalizesPathsReturnedByViewLocationExpanders()
|
||||
{
|
||||
// Arrange
|
||||
var expected =
|
||||
@"Layout
|
||||
Page
|
||||
Partial";
|
||||
|
||||
// Act
|
||||
var responseContent = await Client.GetStringAsync("/BackSlash");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, responseContent.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
public class DefaultRazorPageFactoryProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateFactory_ReturnsExpirationTokensFromCompilerCache_ForUnsuccessfulResults()
|
||||
public void CreateFactory_ReturnsViewDescriptor_ForUnsuccessfulResults()
|
||||
{
|
||||
// Arrange
|
||||
var path = "/file-does-not-exist";
|
||||
|
|
@ -39,11 +39,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
|
||||
// Assert
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal(expirationTokens, result.ExpirationTokens);
|
||||
Assert.Same(descriptor, result.ViewDescriptor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateFactory_ReturnsExpirationTokensFromCompilerCache_ForSuccessfulResults()
|
||||
public void CreateFactory_ReturnsViewDescriptor_ForSuccessfulResults()
|
||||
{
|
||||
// Arrange
|
||||
var relativePath = "/file-exists";
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Threading;
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||
|
|
@ -169,15 +170,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/bar/test-view.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => page));
|
||||
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => viewStart2, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => viewStart2));
|
||||
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => viewStart1, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => viewStart1));
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
|
@ -205,11 +206,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/bar/test-view.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => page));
|
||||
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => viewStart, new[] { changeToken }));
|
||||
.Returns(GetPageFactoryResult(() => viewStart, new[] { changeToken }));
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
|
@ -334,15 +335,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/bar/test-view.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => page));
|
||||
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => viewStart2, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => viewStart2));
|
||||
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => viewStart1, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => viewStart1));
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
|
@ -370,7 +371,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("fake-path1/bar/test-view.rzr"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -398,7 +399,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(expectedViewName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -427,7 +428,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(expectedViewName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -453,7 +454,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(expectedViewName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -481,7 +482,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(viewName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -509,7 +510,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(expectedViewName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -537,7 +538,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(expectedViewName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -565,10 +566,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var nonAreaPage = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Areas/Admin/Views/Home/Index.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => areaPage, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => areaPage));
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/Home/Index.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => nonAreaPage, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => nonAreaPage));
|
||||
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -629,10 +630,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var areaPage2 = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Areas/Marketing/Views/Home/Index.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => areaPage1, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => areaPage1));
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Areas/Sales/Views/Home/Index.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => areaPage2, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => areaPage2));
|
||||
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -693,7 +694,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("test-string/bar.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => Mock.Of<IRazorPage>(), new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => Mock.Of<IRazorPage>()))
|
||||
.Verifiable();
|
||||
|
||||
var expander1Result = new[] { "some-seed" };
|
||||
|
|
@ -746,6 +747,37 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
expander2.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindView_NoramlizesPaths_ReturnedByViewLocationExpanders()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(@"Views\Home\Index.cshtml"))
|
||||
.Returns(GetPageFactoryResult(() => Mock.Of<IRazorPage>()))
|
||||
.Verifiable();
|
||||
|
||||
var expander = new Mock<IViewLocationExpander>();
|
||||
expander
|
||||
.Setup(e => e.ExpandViewLocations(
|
||||
It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.Returns(new[] { @"Views\Home\Index.cshtml" });
|
||||
|
||||
var viewEngine = CreateViewEngine(
|
||||
pageFactory.Object,
|
||||
new[] { expander.Object });
|
||||
var context = GetActionContext(new Dictionary<string, object>());
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "test-view", isMainPage: true);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Success);
|
||||
Assert.IsAssignableFrom<IView>(result.View);
|
||||
pageFactory.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindView_CachesValuesIfViewWasFound()
|
||||
{
|
||||
|
|
@ -754,11 +786,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(factory: null))
|
||||
.Verifiable();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/Shared/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
|
|
@ -795,7 +827,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/Shared/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
|
|
@ -838,16 +870,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
pageFactory
|
||||
.InSequence(sequence)
|
||||
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(new[] { changeToken }));
|
||||
.Returns(GetPageFactoryResult(factory: null, changeTokens: new[] { changeToken }));
|
||||
pageFactory
|
||||
.InSequence(sequence)
|
||||
.Setup(p => p.CreateFactory("/Views/Shared/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page1, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page1))
|
||||
.Verifiable();
|
||||
pageFactory
|
||||
.InSequence(sequence)
|
||||
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page2, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => page2));
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
|
@ -886,20 +918,20 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
pageFactory
|
||||
.InSequence(sequence)
|
||||
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page1, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => page1));
|
||||
pageFactory
|
||||
.InSequence(sequence)
|
||||
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(new[] { changeToken }))
|
||||
.Returns(GetPageFactoryResult(factory: null, changeTokens: new[] { changeToken }))
|
||||
.Verifiable();
|
||||
pageFactory
|
||||
.InSequence(sequence)
|
||||
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page2, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => page2));
|
||||
pageFactory
|
||||
.InSequence(sequence)
|
||||
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => viewStart, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => viewStart));
|
||||
|
||||
var fileProvider = new TestFileProvider();
|
||||
var razorProject = new FileProviderRazorProject(fileProvider);
|
||||
|
|
@ -993,7 +1025,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("viewlocation3"))
|
||||
.Returns(new RazorPageFactoryResult(new[] { changeToken }));
|
||||
.Returns(GetPageFactoryResult(factory: null, changeTokens: new[] { changeToken }));
|
||||
var expander = new Mock<IViewLocationExpander>();
|
||||
var expandedLocations = new[]
|
||||
{
|
||||
|
|
@ -1031,7 +1063,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
// Act - 2
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("viewlocation3"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => page));
|
||||
cancellationTokenSource.Cancel();
|
||||
result = viewEngine.FindView(context, "MyView", isMainPage: true);
|
||||
|
||||
|
|
@ -1131,7 +1163,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("expanded-path/bar-layout"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
|
||||
var expander = new Mock<IViewLocationExpander>();
|
||||
|
|
@ -1210,7 +1242,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/Foo/details.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page.Object, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page.Object))
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
|
|
@ -1339,10 +1371,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||
|
||||
var relativePath = "/Views/Foo/details.cshtml";
|
||||
var factoryResult = GetPageFactoryResult(() => Mock.Of<IRazorPage>());
|
||||
factoryResult.ViewDescriptor.IsPrecompiled = true;
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(relativePath))
|
||||
.Returns(new RazorPageFactoryResult(() => Mock.Of<IRazorPage>(), new IChangeToken[0], isPrecompiled: true))
|
||||
.Returns(factoryResult)
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = new RazorViewEngine(
|
||||
|
|
@ -1375,7 +1409,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(pageName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -1403,7 +1437,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(expectedPageName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -1431,7 +1465,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(expectedPageName))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Returns(GetPageFactoryResult(() => page))
|
||||
.Verifiable();
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
|
|
@ -1761,11 +1795,25 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
|||
var pageFactory = new Mock<IRazorPageFactoryProvider>(MockBehavior.Strict);
|
||||
pageFactory
|
||||
.Setup(f => f.CreateFactory(It.IsAny<string>()))
|
||||
.Returns(new RazorPageFactoryResult(() => Mock.Of<IRazorPage>(), new IChangeToken[0]));
|
||||
.Returns(GetPageFactoryResult(() => Mock.Of<IRazorPage>()));
|
||||
|
||||
return CreateViewEngine(pageFactory.Object);
|
||||
}
|
||||
|
||||
private static RazorPageFactoryResult GetPageFactoryResult(
|
||||
Func<IRazorPage> factory,
|
||||
IList<IChangeToken> changeTokens = null,
|
||||
string path = "/Views/Home/Index.cshtml")
|
||||
{
|
||||
var descriptor = new CompiledViewDescriptor
|
||||
{
|
||||
ExpirationTokens = changeTokens ?? Array.Empty<IChangeToken>(),
|
||||
RelativePath = path,
|
||||
};
|
||||
|
||||
return new RazorPageFactoryResult(descriptor, factory);
|
||||
}
|
||||
|
||||
private TestableRazorViewEngine CreateViewEngine(
|
||||
IRazorPageFactoryProvider pageFactory = null,
|
||||
IEnumerable<IViewLocationExpander> expanders = null,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
|
@ -255,10 +256,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
|||
v.Write("layout-content" + Environment.NewLine);
|
||||
v.RenderBodyPublic();
|
||||
});
|
||||
var pageFactoryResult = new RazorPageFactoryResult(new CompiledViewDescriptor(), () => layout);
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(LayoutPath))
|
||||
.Returns(new RazorPageFactoryResult(() => layout, new IChangeToken[0]));
|
||||
.Returns(pageFactoryResult);
|
||||
|
||||
var viewEngine = new Mock<IRazorViewEngine>(MockBehavior.Strict);
|
||||
viewEngine
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
|
@ -181,10 +182,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
razorPageFactoryProvider
|
||||
.Setup(f => f.CreateFactory("/Home/Path1/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(factory1, new IChangeToken[0]));
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), factory1));
|
||||
razorPageFactoryProvider
|
||||
.Setup(f => f.CreateFactory("/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(factory2, new[] { Mock.Of<IChangeToken>() }));
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), factory2));
|
||||
|
||||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile("/Home/Path1/_ViewStart.cshtml", "content1");
|
||||
|
|
@ -215,7 +216,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Equal(new[] { factory2, factory1 }, entry.ViewStartFactories);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_CachesEntries()
|
||||
{
|
||||
|
|
@ -347,19 +347,19 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var mock = new Mock<IRazorPageFactoryProvider>(MockBehavior.Strict);
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/Pages/Level1/Level2/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), () => null))
|
||||
.Verifiable();
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/Pages/Level1/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), () => null))
|
||||
.Verifiable();
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/Pages/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), () => null))
|
||||
.Verifiable();
|
||||
mock
|
||||
.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), () => null))
|
||||
.Verifiable();
|
||||
|
||||
var razorPageFactoryProvider = mock.Object;
|
||||
|
|
@ -398,13 +398,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(f => f.CreateFactory("/Views/Deeper/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new IChangeToken[0]));
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), () => null));
|
||||
pageFactory
|
||||
.Setup(f => f.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(new IChangeToken[0]));
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), razorPageFactory: null));
|
||||
pageFactory
|
||||
.Setup(f => f.CreateFactory("/_ViewStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new IChangeToken[0]));
|
||||
.Returns(new RazorPageFactoryResult(new CompiledViewDescriptor(), () => null));
|
||||
|
||||
// No files
|
||||
var fileProvider = new TestFileProvider();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace RazorWebSite.Controllers
|
||||
{
|
||||
public class BackSlashController : Controller
|
||||
{
|
||||
public IActionResult Index() => View(@"Views\BackSlash\BackSlashView.cshtml");
|
||||
}
|
||||
}
|
||||
|
|
@ -19,5 +19,6 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(AspNetCoreVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="$(AspNetCoreVersion)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace RazorWebSite
|
||||
{
|
||||
public class ForwardSlashExpander : IViewLocationExpander
|
||||
{
|
||||
public void PopulateValues(ViewLocationExpanderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
|
||||
{
|
||||
if (context.ActionContext is ViewContext viewContext && (string)viewContext.ViewData["back-slash"] == "true")
|
||||
{
|
||||
return new[] { $@"Views\BackSlash\{context.ViewName}.cshtml" };
|
||||
|
||||
}
|
||||
|
||||
return viewLocations;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ namespace RazorWebSite
|
|||
$"{nameof(RazorWebSite)}.EmbeddedViews"));
|
||||
options.FileProviders.Add(updateableFileProvider);
|
||||
options.ViewLocationExpanders.Add(new NonMainPageViewLocationExpander());
|
||||
options.ViewLocationExpanders.Add(new ForwardSlashExpander());
|
||||
})
|
||||
.AddViewOptions(options =>
|
||||
{
|
||||
|
|
@ -51,6 +52,7 @@ namespace RazorWebSite
|
|||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseRequestLocalization(new RequestLocalizationOptions
|
||||
{
|
||||
DefaultRequestCulture = new RequestCulture("en-GB", "en-US"),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
@{
|
||||
ViewData["back-slash"] = "true";
|
||||
Layout = "_Layout";
|
||||
}
|
||||
Page
|
||||
@Html.Partial("_BackSlashPartial")
|
||||
|
|
@ -0,0 +1 @@
|
|||
Partial
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Layout
|
||||
@RenderBody()
|
||||
Loading…
Reference in New Issue