Use RazorPagesOptions.RootDirectory when looking for page hierarchies.
Fixes #5915
This commit is contained in:
parent
de25357c28
commit
bee1a55cff
|
|
@ -35,6 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
private readonly IModelMetadataProvider _modelMetadataProvider;
|
||||
private readonly ITempDataDictionaryFactory _tempDataFactory;
|
||||
private readonly HtmlHelperOptions _htmlHelperOptions;
|
||||
private readonly RazorPagesOptions _razorPagesOptions;
|
||||
private readonly IPageHandlerMethodSelector _selector;
|
||||
private readonly TempDataPropertyProvider _propertyProvider;
|
||||
private readonly RazorProject _razorProject;
|
||||
|
|
@ -53,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
ITempDataDictionaryFactory tempDataFactory,
|
||||
IOptions<MvcOptions> mvcOptions,
|
||||
IOptions<HtmlHelperOptions> htmlHelperOptions,
|
||||
IOptions<RazorPagesOptions> razorPagesOptions,
|
||||
IPageHandlerMethodSelector selector,
|
||||
TempDataPropertyProvider propertyProvider,
|
||||
RazorProject razorProject,
|
||||
|
|
@ -69,6 +71,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
_modelMetadataProvider = modelMetadataProvider;
|
||||
_tempDataFactory = tempDataFactory;
|
||||
_htmlHelperOptions = htmlHelperOptions.Value;
|
||||
_razorPagesOptions = razorPagesOptions.Value;
|
||||
_selector = selector;
|
||||
_propertyProvider = propertyProvider;
|
||||
_razorProject = razorProject;
|
||||
|
|
@ -201,7 +204,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
internal List<Func<IRazorPage>> GetPageStartFactories(CompiledPageActionDescriptor descriptor)
|
||||
{
|
||||
var pageStartFactories = new List<Func<IRazorPage>>();
|
||||
var pageStartItems = _razorProject.FindHierarchicalItems(descriptor.ViewEnginePath, PageStartFileName);
|
||||
var pageStartItems = _razorProject.FindHierarchicalItems(
|
||||
_razorPagesOptions.RootDirectory,
|
||||
descriptor.RelativePath,
|
||||
PageStartFileName);
|
||||
foreach (var item in pageStartItems)
|
||||
{
|
||||
if (item.Exists)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
|
@ -248,6 +249,35 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("/Login?ReturnUrl=%2FHelloWorldWithAuth", response.Headers.Location.PathAndQuery);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task PageStart_IsDiscoveredWhenRootDirectoryIsNotSpecified()
|
||||
{
|
||||
// Test for https://github.com/aspnet/Mvc/issues/5915
|
||||
//Arrange
|
||||
var expected = $"Hello from _PageStart{Environment.NewLine}Hello from /Pages/WithPageStart/Index.cshtml!";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetStringAsync("/Pages/WithPageStart");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, response.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PageImport_IsDiscoveredWhenRootDirectoryIsNotSpecified()
|
||||
{
|
||||
// Test for https://github.com/aspnet/Mvc/issues/5915
|
||||
//Arrange
|
||||
var expected = "Hello from CustomService!";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetStringAsync("/Pages/WithPageImport");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, response.Trim());
|
||||
}
|
||||
|
||||
private static string GetCookie(HttpResponseMessage response)
|
||||
{
|
||||
var setCookie = response.Headers.GetValues("Set-Cookie").ToArray();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -113,5 +114,33 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuthFolder", response.Headers.Location.PathAndQuery);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PageStart_IsDiscoveredWhenRootDirectoryIsSpecified()
|
||||
{
|
||||
// Test for https://github.com/aspnet/Mvc/issues/5915
|
||||
//Arrange
|
||||
var expected = $"Hello from _PageStart{Environment.NewLine}Hello from /Pages/WithPageStart/Index.cshtml!";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetStringAsync("/WithPageStart");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, response.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PageImport_IsDiscoveredWhenRootDirectoryIsSpecified()
|
||||
{
|
||||
// Test for https://github.com/aspnet/Mvc/issues/5915
|
||||
//Arrange
|
||||
var expected = "Hello from CustomService!";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetStringAsync("/WithPageImport");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, response.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,10 @@ using Microsoft.AspNetCore.Mvc.Filters;
|
|||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.Evolution;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Moq;
|
||||
|
|
@ -574,7 +572,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
RelativePath = "/Views/Deeper/Index.cshtml",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||
};
|
||||
|
|
@ -623,13 +621,72 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
mock.Verify();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("/Pages/Level1/")]
|
||||
[InlineData("/Pages/Level1")]
|
||||
public void GetPageFactories_DoesNotFindPageStartsOutsideBaseDirectory(string rootDirectory)
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "/Pages/Level1/Level2/Index.cshtml",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Pages/Level1/Level2/Index.cshtml"
|
||||
};
|
||||
var compiledPageDescriptor = new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
PageTypeInfo = typeof(object).GetTypeInfo(),
|
||||
};
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(compiledPageDescriptor);
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
|
||||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile("/_PageStart.cshtml", "page content");
|
||||
fileProvider.AddFile("/Pages/_PageStart.cshtml", "page content");
|
||||
fileProvider.AddFile("/Pages/Level1/_PageStart.cshtml", "page content");
|
||||
fileProvider.AddFile("/Pages/Level1/Level2/_PageStart.cshtml", "page content");
|
||||
fileProvider.AddFile("/Pages/Level1/Level3/_PageStart.cshtml", "page content");
|
||||
|
||||
var razorProject = new TestRazorProject(fileProvider);
|
||||
|
||||
var mock = new Mock<IRazorPageFactoryProvider>(MockBehavior.Strict);
|
||||
mock.Setup(p => p.CreateFactory("/Pages/Level1/Level2/_PageStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Verifiable();
|
||||
mock.Setup(p => p.CreateFactory("/Pages/Level1/_PageStart.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => null, new List<IChangeToken>()))
|
||||
.Verifiable();
|
||||
var razorPageFactoryProvider = mock.Object;
|
||||
var options = new RazorPagesOptions
|
||||
{
|
||||
RootDirectory = rootDirectory,
|
||||
};
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
razorPageFactoryProvider: razorPageFactoryProvider,
|
||||
razorProject: razorProject,
|
||||
razorPagesOptions: options);
|
||||
|
||||
// Act
|
||||
var factories = invokerProvider.GetPageStartFactories(compiledPageDescriptor);
|
||||
|
||||
// Assert
|
||||
mock.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPageStartFactories_NoFactoriesForMissingFiles()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor()
|
||||
{
|
||||
RelativePath = "Path1",
|
||||
RelativePath = "/Views/Deeper/Index.cshtml",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||
};
|
||||
|
|
@ -692,7 +749,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
IPageFactoryProvider pageProvider = null,
|
||||
IPageModelFactoryProvider modelProvider = null,
|
||||
IRazorPageFactoryProvider razorPageFactoryProvider = null,
|
||||
RazorProject razorProject = null)
|
||||
RazorProject razorProject = null,
|
||||
RazorPagesOptions razorPagesOptions = null)
|
||||
{
|
||||
var tempDataFactory = new Mock<ITempDataDictionaryFactory>();
|
||||
tempDataFactory.Setup(t => t.GetTempData(It.IsAny<HttpContext>()))
|
||||
|
|
@ -714,6 +772,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
tempDataFactory.Object,
|
||||
new TestOptionsManager<MvcOptions>(),
|
||||
new TestOptionsManager<HtmlHelperOptions>(),
|
||||
new TestOptionsManager<RazorPagesOptions>(razorPagesOptions ?? new RazorPagesOptions()),
|
||||
Mock.Of<IPageHandlerMethodSelector>(),
|
||||
new TempDataPropertyProvider(),
|
||||
razorProject,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
public class TestOptionsManager<T> : OptionsManager<T>
|
||||
where T : class, new()
|
||||
public class TestOptionsManager<TOptions> : IOptions<TOptions>
|
||||
where TOptions : class, new()
|
||||
{
|
||||
public TestOptionsManager()
|
||||
: base(Enumerable.Empty<IConfigureOptions<T>>())
|
||||
: this(new TOptions())
|
||||
{
|
||||
}
|
||||
|
||||
public TestOptionsManager(TOptions value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public TOptions Value { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
@page
|
||||
Hello from @CustomService.Value!
|
||||
|
|
@ -0,0 +1 @@
|
|||
@using CustomNamespace
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
@page
|
||||
Hello from @Path!
|
||||
|
|
@ -0,0 +1 @@
|
|||
Hello from _PageStart
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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.
|
||||
|
||||
namespace CustomNamespace
|
||||
{
|
||||
public static class CustomService
|
||||
{
|
||||
public static string Value => nameof(CustomService);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue