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 IModelMetadataProvider _modelMetadataProvider;
|
||||||
private readonly ITempDataDictionaryFactory _tempDataFactory;
|
private readonly ITempDataDictionaryFactory _tempDataFactory;
|
||||||
private readonly HtmlHelperOptions _htmlHelperOptions;
|
private readonly HtmlHelperOptions _htmlHelperOptions;
|
||||||
|
private readonly RazorPagesOptions _razorPagesOptions;
|
||||||
private readonly IPageHandlerMethodSelector _selector;
|
private readonly IPageHandlerMethodSelector _selector;
|
||||||
private readonly TempDataPropertyProvider _propertyProvider;
|
private readonly TempDataPropertyProvider _propertyProvider;
|
||||||
private readonly RazorProject _razorProject;
|
private readonly RazorProject _razorProject;
|
||||||
|
|
@ -53,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
ITempDataDictionaryFactory tempDataFactory,
|
ITempDataDictionaryFactory tempDataFactory,
|
||||||
IOptions<MvcOptions> mvcOptions,
|
IOptions<MvcOptions> mvcOptions,
|
||||||
IOptions<HtmlHelperOptions> htmlHelperOptions,
|
IOptions<HtmlHelperOptions> htmlHelperOptions,
|
||||||
|
IOptions<RazorPagesOptions> razorPagesOptions,
|
||||||
IPageHandlerMethodSelector selector,
|
IPageHandlerMethodSelector selector,
|
||||||
TempDataPropertyProvider propertyProvider,
|
TempDataPropertyProvider propertyProvider,
|
||||||
RazorProject razorProject,
|
RazorProject razorProject,
|
||||||
|
|
@ -69,6 +71,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
_modelMetadataProvider = modelMetadataProvider;
|
_modelMetadataProvider = modelMetadataProvider;
|
||||||
_tempDataFactory = tempDataFactory;
|
_tempDataFactory = tempDataFactory;
|
||||||
_htmlHelperOptions = htmlHelperOptions.Value;
|
_htmlHelperOptions = htmlHelperOptions.Value;
|
||||||
|
_razorPagesOptions = razorPagesOptions.Value;
|
||||||
_selector = selector;
|
_selector = selector;
|
||||||
_propertyProvider = propertyProvider;
|
_propertyProvider = propertyProvider;
|
||||||
_razorProject = razorProject;
|
_razorProject = razorProject;
|
||||||
|
|
@ -201,7 +204,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
internal List<Func<IRazorPage>> GetPageStartFactories(CompiledPageActionDescriptor descriptor)
|
internal List<Func<IRazorPage>> GetPageStartFactories(CompiledPageActionDescriptor descriptor)
|
||||||
{
|
{
|
||||||
var pageStartFactories = new List<Func<IRazorPage>>();
|
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)
|
foreach (var item in pageStartItems)
|
||||||
{
|
{
|
||||||
if (item.Exists)
|
if (item.Exists)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
@ -248,6 +249,35 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal("/Login?ReturnUrl=%2FHelloWorldWithAuth", response.Headers.Location.PathAndQuery);
|
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)
|
private static string GetCookie(HttpResponseMessage response)
|
||||||
{
|
{
|
||||||
var setCookie = response.Headers.GetValues("Set-Cookie").ToArray();
|
var setCookie = response.Headers.GetValues("Set-Cookie").ToArray();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -113,5 +114,33 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||||
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuthFolder", response.Headers.Location.PathAndQuery);
|
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.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor;
|
using Microsoft.AspNetCore.Mvc.Razor;
|
||||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.FileProviders;
|
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -574,7 +572,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptor = new PageActionDescriptor()
|
var descriptor = new PageActionDescriptor()
|
||||||
{
|
{
|
||||||
RelativePath = "Path1",
|
RelativePath = "/Views/Deeper/Index.cshtml",
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
};
|
};
|
||||||
|
|
@ -623,13 +621,72 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
mock.Verify();
|
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]
|
[Fact]
|
||||||
public void GetPageStartFactories_NoFactoriesForMissingFiles()
|
public void GetPageStartFactories_NoFactoriesForMissingFiles()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptor = new PageActionDescriptor()
|
var descriptor = new PageActionDescriptor()
|
||||||
{
|
{
|
||||||
RelativePath = "Path1",
|
RelativePath = "/Views/Deeper/Index.cshtml",
|
||||||
FilterDescriptors = new FilterDescriptor[0],
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
ViewEnginePath = "/Views/Deeper/Index.cshtml"
|
||||||
};
|
};
|
||||||
|
|
@ -692,7 +749,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
IPageFactoryProvider pageProvider = null,
|
IPageFactoryProvider pageProvider = null,
|
||||||
IPageModelFactoryProvider modelProvider = null,
|
IPageModelFactoryProvider modelProvider = null,
|
||||||
IRazorPageFactoryProvider razorPageFactoryProvider = null,
|
IRazorPageFactoryProvider razorPageFactoryProvider = null,
|
||||||
RazorProject razorProject = null)
|
RazorProject razorProject = null,
|
||||||
|
RazorPagesOptions razorPagesOptions = null)
|
||||||
{
|
{
|
||||||
var tempDataFactory = new Mock<ITempDataDictionaryFactory>();
|
var tempDataFactory = new Mock<ITempDataDictionaryFactory>();
|
||||||
tempDataFactory.Setup(t => t.GetTempData(It.IsAny<HttpContext>()))
|
tempDataFactory.Setup(t => t.GetTempData(It.IsAny<HttpContext>()))
|
||||||
|
|
@ -714,6 +772,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
tempDataFactory.Object,
|
tempDataFactory.Object,
|
||||||
new TestOptionsManager<MvcOptions>(),
|
new TestOptionsManager<MvcOptions>(),
|
||||||
new TestOptionsManager<HtmlHelperOptions>(),
|
new TestOptionsManager<HtmlHelperOptions>(),
|
||||||
|
new TestOptionsManager<RazorPagesOptions>(razorPagesOptions ?? new RazorPagesOptions()),
|
||||||
Mock.Of<IPageHandlerMethodSelector>(),
|
Mock.Of<IPageHandlerMethodSelector>(),
|
||||||
new TempDataPropertyProvider(),
|
new TempDataPropertyProvider(),
|
||||||
razorProject,
|
razorProject,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc
|
namespace Microsoft.AspNetCore.Mvc
|
||||||
{
|
{
|
||||||
public class TestOptionsManager<T> : OptionsManager<T>
|
public class TestOptionsManager<TOptions> : IOptions<TOptions>
|
||||||
where T : class, new()
|
where TOptions : class, new()
|
||||||
{
|
{
|
||||||
public TestOptionsManager()
|
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