Add a PagesOption type that allows configuring the root for Page file discovery

Fixes #5785
This commit is contained in:
Pranav K 2017-03-02 17:23:55 -08:00
parent 85e28ae478
commit 145d27f9b3
22 changed files with 581 additions and 29 deletions

View File

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<TargetFrameworks>net452;netcoreapp1.1</TargetFrameworks>
<TargetFrameworks>netcoreapp1.1;net452</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp1.1</TargetFrameworks>
</PropertyGroup>

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
@ -29,21 +28,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
public override IEnumerable<RazorProjectItem> EnumerateItems(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (path.Length == 0 || path[0] != '/')
{
throw new ArgumentException(Resources.RazorProject_PathMustStartWithForwardSlash);
}
return EnumerateFiles(_provider.GetDirectoryContents(path), path, "");
EnsureValidPath(path);
return EnumerateFiles(_provider.GetDirectoryContents(path), path, prefix: string.Empty);
}
public virtual IChangeToken Watch(string pattern) => _provider.Watch(pattern);
private IEnumerable<RazorProjectItem> EnumerateFiles(IDirectoryContents directory, string basePath, string prefix)
{
if (directory.Exists)
@ -53,7 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
if (file.IsDirectory)
{
var relativePath = prefix + "/" + file.Name;
var subDirectory = _provider.GetDirectoryContents(relativePath);
var subDirectory = _provider.GetDirectoryContents(JoinPath(basePath, relativePath));
var children = EnumerateFiles(subDirectory, basePath, relativePath);
foreach (var child in children)
{
@ -67,5 +55,21 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
}
}
}
private static string JoinPath(string path1, string path2)
{
var hasTrailingSlash = path1.EndsWith("/", StringComparison.Ordinal);
var hasLeadingSlash = path2.StartsWith("/", StringComparison.Ordinal);
if (hasLeadingSlash && hasTrailingSlash)
{
return path1 + path2.Substring(1);
}
else if (hasLeadingSlash || hasTrailingSlash)
{
return path1 + path2;
}
return path1 + "/" + path2;
}
}
}

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
public void OnProvidersExecuting(ActionDescriptorProviderContext context)
{
foreach (var item in _project.EnumerateItems("/"))
foreach (var item in _project.EnumerateItems(_pagesOptions.RootDirectory))
{
if (item.Filename.StartsWith("_"))
{
@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
private void AddActionDescriptors(IList<ActionDescriptor> actions, RazorProjectItem item, string template)
{
var model = new PageApplicationModel(item.CombinedPath, item.PathWithoutExtension);
var routePrefix = item.BasePath == "/" ? item.PathWithoutExtension : item.BasePath + item.PathWithoutExtension;
var routePrefix = item.PathWithoutExtension;
model.Selectors.Add(CreateSelectorModel(routePrefix, template));
if (string.Equals(IndexFileName, item.Filename, StringComparison.OrdinalIgnoreCase))

View File

@ -1,22 +1,38 @@
// 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 Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
public class PageActionDescriptorChangeProvider : IActionDescriptorChangeProvider
{
private readonly RazorProject _razorProject;
private readonly IFileProvider _fileProvider;
private readonly string _searchPattern;
public PageActionDescriptorChangeProvider(RazorProject razorProject)
public PageActionDescriptorChangeProvider(
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
IOptions<RazorPagesOptions> razorPagesOptions)
{
_razorProject = razorProject;
if (fileProviderAccessor == null)
{
throw new ArgumentNullException(nameof(fileProviderAccessor));
}
if (razorPagesOptions == null)
{
throw new ArgumentNullException(nameof(razorPagesOptions));
}
_fileProvider = fileProviderAccessor.FileProvider;
_searchPattern = razorPagesOptions.Value.RootDirectory.TrimEnd('/') + "/**/*.cshtml";
}
public IChangeToken GetChangeToken() => ((DefaultRazorProject)_razorProject).Watch("**/*.cshtml");
public IChangeToken GetChangeToken() => _fileProvider.Watch(_searchPattern);
}
}

View File

@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
}
/// <summary>
/// Unsupported handler method type '{0}'.
/// Unsupported handler method return type '{0}'.
/// </summary>
internal static string UnsupportedHandlerMethodType
{
@ -99,13 +99,29 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
}
/// <summary>
/// Unsupported handler method type '{0}'.
/// Unsupported handler method return type '{0}'.
/// </summary>
internal static string FormatUnsupportedHandlerMethodType(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("UnsupportedHandlerMethodType"), p0);
}
/// <summary>
/// Path must be an application relative path that starts with a forward slash '/'.
/// </summary>
internal static string PathMustBeAnAppRelativePath
{
get { return GetString("PathMustBeAnAppRelativePath"); }
}
/// <summary>
/// Path must be an application relative path that starts with a forward slash '/'.
/// </summary>
internal static string FormatPathMustBeAnAppRelativePath()
{
return GetString("PathMustBeAnAppRelativePath");
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -12,10 +12,34 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
/// </summary>
public class RazorPagesOptions
{
private string _root = "/";
/// <summary>
/// Gets a list of <see cref="IPageApplicationModelConvention"/> instances that will be applied to
/// the <see cref="PageModel"/> when discovering Razor Pages.
/// </summary>
public IList<IPageApplicationModelConvention> Conventions { get; } = new List<IPageApplicationModelConvention>();
/// <summary>
/// Application relative path used as the root of discovery for Razor Page files.
/// </summary>
public string RootDirectory
{
get => _root;
set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(value));
}
if (value[0] != '/')
{
throw new ArgumentException(Resources.PathMustBeAnAppRelativePath, nameof(value));
}
_root = value;
}
}
}
}

View File

@ -135,4 +135,7 @@
<data name="UnsupportedHandlerMethodType" xml:space="preserve">
<value>Unsupported handler method return type '{0}'.</value>
</data>
<data name="PathMustBeAnAppRelativePath" xml:space="preserve">
<value>Path must be an application relative path that starts with a forward slash '/'.</value>
</data>
</root>

View File

@ -35,6 +35,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public async Task HelloWorld_CanGetContent()
{
// Arrange
// Note: If the route in this test case ever changes, the negative test case
// RazorPagesWithBasePathTest.PageOutsideBasePath_IsNotRouteable needs to be updated too.
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/HelloWorld");
// Act

View File

@ -0,0 +1,117 @@
// 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.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class RazorPagesWithBasePathTest : IClassFixture<MvcTestFixture<RazorPagesWebSite.StartupWithBasePath>>
{
public RazorPagesWithBasePathTest(MvcTestFixture<RazorPagesWebSite.StartupWithBasePath> fixture)
{
Client = fixture.Client;
}
public HttpClient Client { get; }
[Fact]
public async Task PageOutsideBasePath_IsNotRouteable()
{
// Act
var response = await Client.GetAsync("/HelloWorld");
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task IndexAtBasePath_IsRouteableAtRoot()
{
// Act
var response = await Client.GetAsync("/");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello from /Index", content.Trim());
}
[Fact]
public async Task IndexAtBasePath_IsRouteableViaIndex()
{
// Act
var response = await Client.GetAsync("/Index");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello from /Index", content.Trim());
}
[Fact]
public async Task IndexInSubdirectory_IsRouteableViaDirectoryName()
{
// Act
var response = await Client.GetAsync("/Admin/Index");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello from /Admin/Index", content.Trim());
}
[Fact]
public async Task PageWithRouteTemplateInSubdirectory_IsRouteable()
{
// Act
var response = await Client.GetAsync("/Admin/RouteTemplate/1/MyRouteSuffix/");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello from /Admin/RouteTemplate 1", content.Trim());
}
[Fact]
public async Task PageWithRouteTemplateInSubdirectory_IsRouteable_WithOptionalParameters()
{
// Act
var response = await Client.GetAsync("/Admin/RouteTemplate/my-user-id/MyRouteSuffix/4");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello from /Admin/RouteTemplate my-user-id 4", content.Trim());
}
[Fact]
public async Task AuthConvention_IsAppliedOnBasePathRelativePaths_ForFiles()
{
// Act
var response = await Client.GetAsync("/Conventions/Auth");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuth", response.Headers.Location.PathAndQuery);
}
[Fact]
public async Task AuthConvention_IsAppliedOnBasePathRelativePaths_For_Folders()
{
// Act
var response = await Client.GetAsync("/Conventions/AuthFolder");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("/Login?ReturnUrl=%2FConventions%2FAuthFolder", response.Headers.Location.PathAndQuery);
}
}
}

View File

@ -0,0 +1,129 @@
// 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.FileProviders;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
public class DefaultRazorProjectTest
{
[Fact]
public void EnumerateFiles_ReturnsEmptySequenceIfNoCshtmlFilesArePresent()
{
// Arrange
var fileProvider = new TestFileProvider();
var file1 = fileProvider.AddFile("File1.txt", "content");
var file2 = fileProvider.AddFile("File2.js", "content");
fileProvider.AddDirectoryContent("/", new IFileInfo[] { file1, file2 });
var razorProject = new DefaultRazorProject(fileProvider);
// Act
var razorFiles = razorProject.EnumerateItems("/");
// Assert
Assert.Empty(razorFiles);
}
[Fact]
public void EnumerateFiles_ReturnsCshtmlFiles()
{
// Arrange
var fileProvider = new TestFileProvider();
var file1 = fileProvider.AddFile("File1.cshtml", "content");
var file2 = fileProvider.AddFile("File2.js", "content");
var file3 = fileProvider.AddFile("File3.cshtml", "content");
fileProvider.AddDirectoryContent("/", new IFileInfo[] { file1, file2, file3 });
var razorProject = new DefaultRazorProject(fileProvider);
// Act
var razorFiles = razorProject.EnumerateItems("/");
// Assert
Assert.Collection(razorFiles.OrderBy(f => f.Path),
file => Assert.Equal("/File1.cshtml", file.Path),
file => Assert.Equal("/File3.cshtml", file.Path));
}
[Fact]
public void EnumerateFiles_IteratesOverAllCshtmlUnderRoot()
{
// Arrange
var fileProvider = new TestFileProvider();
var directory1 = new TestDirectoryFileInfo
{
Name = "Level1-Dir1",
};
var file1 = fileProvider.AddFile("File1.cshtml", "content");
var directory2 = new TestDirectoryFileInfo
{
Name = "Level1-Dir2",
};
fileProvider.AddDirectoryContent("/", new IFileInfo[] { directory1, file1, directory2 });
var file2 = fileProvider.AddFile("Level1-Dir1/File2.cshtml", "content");
var file3 = fileProvider.AddFile("Level1-Dir1/File3.cshtml", "content");
var file4 = fileProvider.AddFile("Level1-Dir1/File4.txt", "content");
var directory3 = new TestDirectoryFileInfo
{
Name = "Level2-Dir1"
};
fileProvider.AddDirectoryContent("/Level1-Dir1", new IFileInfo[] { file2, directory3, file3, file4 });
var file5 = fileProvider.AddFile("Level1-Dir2/File5.cshtml", "content");
fileProvider.AddDirectoryContent("/Level1-Dir2", new IFileInfo[] { file5 });
fileProvider.AddDirectoryContent("/Level1/Level2", new IFileInfo[0]);
var razorProject = new DefaultRazorProject(fileProvider);
// Act
var razorFiles = razorProject.EnumerateItems("/");
// Assert
Assert.Collection(razorFiles.OrderBy(f => f.Path),
file => Assert.Equal("/File1.cshtml", file.Path),
file => Assert.Equal("/Level1-Dir1/File2.cshtml", file.Path),
file => Assert.Equal("/Level1-Dir1/File3.cshtml", file.Path),
file => Assert.Equal("/Level1-Dir2/File5.cshtml", file.Path));
}
[Fact]
public void EnumerateFiles_IteratesOverAllCshtmlUnderPath()
{
// Arrange
var fileProvider = new TestFileProvider();
var directory1 = new TestDirectoryFileInfo
{
Name = "Level1-Dir1",
};
var file1 = fileProvider.AddFile("File1.cshtml", "content");
var directory2 = new TestDirectoryFileInfo
{
Name = "Level1-Dir2",
};
fileProvider.AddDirectoryContent("/", new IFileInfo[] { directory1, file1, directory2 });
var file2 = fileProvider.AddFile("Level1-Dir1/File2.cshtml", "content");
var file3 = fileProvider.AddFile("Level1-Dir1/File3.cshtml", "content");
var file4 = fileProvider.AddFile("Level1-Dir1/File4.txt", "content");
var directory3 = new TestDirectoryFileInfo
{
Name = "Level2-Dir1"
};
fileProvider.AddDirectoryContent("/Level1-Dir1", new IFileInfo[] { file2, directory3, file3, file4 });
var file5 = fileProvider.AddFile("Level1-Dir2/File5.cshtml", "content");
fileProvider.AddDirectoryContent("/Level1-Dir2", new IFileInfo[] { file5 });
fileProvider.AddDirectoryContent("/Level1/Level2", new IFileInfo[0]);
var razorProject = new DefaultRazorProject(fileProvider);
// Act
var razorFiles = razorProject.EnumerateItems("/Level1-Dir1");
// Assert
Assert.Collection(razorFiles.OrderBy(f => f.Path),
file => Assert.Equal("/File2.cshtml", file.Path),
file => Assert.Equal("/File3.cshtml", file.Path));
}
}
}

View File

@ -95,6 +95,75 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
Assert.Equal("Test/Home", descriptor.AttributeRouteInfo.Template);
}
[Fact]
public void GetDescriptors_GeneratesRouteTemplate()
{
// Arrange
var razorProject = new Mock<RazorProject>(MockBehavior.Strict);
razorProject.Setup(p => p.EnumerateItems("/"))
.Returns(new[]
{
GetProjectItem("/", "/base-path/Test.cshtml", $"@page \"Home\" {Environment.NewLine}<h1>Hello world</h1>"),
GetProjectItem("/", "/base-path/Index.cshtml", $"@page {Environment.NewLine}"),
GetProjectItem("/", "/base-path/Admin/Index.cshtml", $"@page{Environment.NewLine}"),
GetProjectItem("/", "/base-path/Admin/User.cshtml", $"@page{Environment.NewLine}"),
});
var options = GetRazorPagesOptions();
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor<MvcOptions>(),
options);
var context = new ActionDescriptorProviderContext();
// Act
provider.OnProvidersExecuting(context);
// Assert
Assert.Collection(context.Results,
result => Assert.Equal("base-path/Test/Home", result.AttributeRouteInfo.Template),
result => Assert.Equal("base-path/Index", result.AttributeRouteInfo.Template),
result => Assert.Equal("base-path", result.AttributeRouteInfo.Template),
result => Assert.Equal("base-path/Admin/Index", result.AttributeRouteInfo.Template),
result => Assert.Equal("base-path/Admin", result.AttributeRouteInfo.Template),
result => Assert.Equal("base-path/Admin/User", result.AttributeRouteInfo.Template));
}
[Fact]
public void GetDescriptors_UsesBasePathOption_WhenGeneratingRouteTemplate()
{
// Arrange
var razorProject = new Mock<RazorProject>(MockBehavior.Strict);
razorProject.Setup(p => p.EnumerateItems("/base-path"))
.Returns(new[]
{
GetProjectItem("/base-path", "/Test.cshtml", $"@page \"Home\" {Environment.NewLine}<h1>Hello world</h1>"),
GetProjectItem("/base-path", "/Index.cshtml", $"@page {Environment.NewLine}"),
GetProjectItem("/base-path", "/Admin/Index.cshtml", $"@page{Environment.NewLine}"),
GetProjectItem("/base-path", "/Admin/User.cshtml", $"@page{Environment.NewLine}"),
});
var options = GetRazorPagesOptions();
options.Value.RootDirectory = "/base-path";
var provider = new PageActionDescriptorProvider(
razorProject.Object,
GetAccessor<MvcOptions>(),
options);
var context = new ActionDescriptorProviderContext();
// Act
provider.OnProvidersExecuting(context);
// Assert
Assert.Collection(context.Results,
result => Assert.Equal("Test/Home", result.AttributeRouteInfo.Template),
result => Assert.Equal("Index", result.AttributeRouteInfo.Template),
result => Assert.Equal("", result.AttributeRouteInfo.Template),
result => Assert.Equal("Admin/Index", result.AttributeRouteInfo.Template),
result => Assert.Equal("Admin", result.AttributeRouteInfo.Template),
result => Assert.Equal("Admin/User", result.AttributeRouteInfo.Template));
}
[Theory]
[InlineData("/Path1")]
[InlineData("~/Path1")]
@ -232,7 +301,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
});
}
[Fact]
public void GetDescriptors_AddsGlobalFilters()
{

View File

@ -0,0 +1,54 @@
// 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.Razor.Internal;
using Microsoft.Extensions.FileProviders;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
public class PageActionDescriptorChangeProviderTest
{
[Fact]
public void GetChangeToken_WatchesAllCshtmlFilesUnderFileSystemRoot()
{
// Arrange
var options = new TestOptionsManager<RazorPagesOptions>();
var fileProvider = new Mock<IFileProvider>();
var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>();
fileProviderAccessor
.Setup(f => f.FileProvider)
.Returns(fileProvider.Object);
var changeProvider = new PageActionDescriptorChangeProvider(fileProviderAccessor.Object, options);
// Act
var changeToken = changeProvider.GetChangeToken();
// Assert
fileProvider.Verify(f => f.Watch("/**/*.cshtml"));
}
[Theory]
[InlineData("/pages-base-dir")]
[InlineData("/pages-base-dir/")]
public void GetChangeToken_WatchesAllCshtmlFilesUnderSpecifiedRootDirectory(string rootDirectory)
{
// Arrange
var options = new TestOptionsManager<RazorPagesOptions>();
options.Value.RootDirectory = rootDirectory;
var fileProvider = new Mock<IFileProvider>();
var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>();
fileProviderAccessor
.Setup(f => f.FileProvider)
.Returns(fileProvider.Object);
var changeProvider = new PageActionDescriptorChangeProvider(fileProviderAccessor.Object, options);
// Act
var changeToken = changeProvider.GetChangeToken();
// Assert
fileProvider.Verify(f => f.Watch("/pages-base-dir/**/*.cshtml"));
}
}
}

View File

@ -0,0 +1,25 @@
// 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;
using System.Collections.Generic;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Mvc.TestCommon
{
public class TestDirectoryContent : IDirectoryContents
{
private readonly IEnumerable<IFileInfo> _files;
public TestDirectoryContent(IEnumerable<IFileInfo> files)
{
_files = files;
}
public bool Exists => true;
public IEnumerator<IFileInfo> GetEnumerator() => _files.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

View File

@ -0,0 +1,30 @@
// 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.IO;
using System.Text;
using Microsoft.Extensions.FileProviders;
namespace Microsoft.AspNetCore.Mvc.Razor
{
public class TestDirectoryFileInfo : IFileInfo
{
public bool IsDirectory => true;
public long Length { get; set; }
public string Name { get; set; }
public string PhysicalPath { get; set; }
public bool Exists => true;
public DateTimeOffset LastModified => throw new NotImplementedException();
public Stream CreateReadStream()
{
throw new NotSupportedException();
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc.TestCommon;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
@ -13,12 +14,20 @@ namespace Microsoft.AspNetCore.Mvc.Razor
{
private readonly Dictionary<string, IFileInfo> _lookup =
new Dictionary<string, IFileInfo>(StringComparer.Ordinal);
private readonly Dictionary<string, IDirectoryContents> _directoryContentsLookup =
new Dictionary<string, IDirectoryContents>();
private readonly Dictionary<string, TestFileChangeToken> _fileTriggers =
new Dictionary<string, TestFileChangeToken>(StringComparer.Ordinal);
public virtual IDirectoryContents GetDirectoryContents(string subpath)
{
throw new NotSupportedException();
if (_directoryContentsLookup.TryGetValue(subpath, out var value))
{
return value;
}
return new NotFoundDirectoryContents();
}
public TestFileInfo AddFile(string path, string contents)
@ -36,6 +45,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return fileInfo;
}
public TestDirectoryContent AddDirectoryContent(string path, IEnumerable<IFileInfo> files)
{
var directoryContent = new TestDirectoryContent(files);
_directoryContentsLookup[path] = directoryContent;
return directoryContent;
}
public void AddFile(string path, IFileInfo contents)
{
_lookup[path] = contents;

View File

@ -0,0 +1,2 @@
@page
Hello from @ViewContext.RouteData.Values["page"]

View File

@ -0,0 +1,2 @@
@page "{id}/MyRouteSuffix/{value:int?}"
Hello from @ViewContext.RouteData.Values["page"] @ViewContext.RouteData.Values["id"] @ViewContext.RouteData.Values["value"]

View File

@ -0,0 +1 @@
@page

View File

@ -0,0 +1 @@
@page

View File

@ -0,0 +1,2 @@
@page
Hello from @ViewContext.RouteData.Values["page"]

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\build\common.props" />
<PropertyGroup>

View File

@ -0,0 +1,40 @@
// 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.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace RazorPagesWebSite
{
public class StartupWithBasePath
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddCookieTempDataProvider()
.AddRazorPagesOptions(options =>
{
options.RootDirectory = "/Pages";
options.AuthorizePage("/Conventions/Auth", string.Empty);
options.AuthorizeFolder("/Conventions/AuthFolder", string.Empty);
});
}
public void Configure(IApplicationBuilder app)
{
app.UseCultureReplacer();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
LoginPath = "/Login",
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
app.UseStaticFiles();
app.UseMvc();
}
}
}