Allow override routes on Pages

Fixes #6605
This commit is contained in:
Pranav K 2018-01-11 14:30:01 -08:00
parent 094b61dfc6
commit 946b64143e
7 changed files with 84 additions and 25 deletions

View File

@ -60,21 +60,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
model.RouteValues.Add("page", model.ViewEnginePath); model.RouteValues.Add("page", model.ViewEnginePath);
if (AttributeRouteModel.IsOverridePattern(routeTemplate))
{
throw new InvalidOperationException(string.Format(
Resources.PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable,
model.RelativePath));
}
var selectorModel = CreateSelectorModel(pageRoute, routeTemplate); var selectorModel = CreateSelectorModel(pageRoute, routeTemplate);
model.Selectors.Add(selectorModel); model.Selectors.Add(selectorModel);
var fileName = Path.GetFileName(model.RelativePath); var fileName = Path.GetFileName(model.RelativePath);
if (string.Equals(IndexFileName, fileName, StringComparison.OrdinalIgnoreCase)) if (!AttributeRouteModel.IsOverridePattern(routeTemplate) &&
string.Equals(IndexFileName, fileName, StringComparison.OrdinalIgnoreCase))
{ {
// For pages ending in /Index.cshtml, we want to allow incoming routing, but // For pages without an override route, and ending in /Index.cshtml, we want to allow
// force outgoing routes to match to the path sans /Index. // incoming routing, but force outgoing routes to match to the path sans /Index.
selectorModel.AttributeRouteModel.SuppressLinkGeneration = true; selectorModel.AttributeRouteModel.SuppressLinkGeneration = true;
var index = pageRoute.LastIndexOf('/'); var index = pageRoute.LastIndexOf('/');

View File

@ -117,9 +117,6 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable" xml:space="preserve">
<value>The route for the page at '{0}' cannot start with / or ~/. Pages do not support overriding the file path of the page.</value>
</data>
<data name="PropertyOfTypeCannotBeNull" xml:space="preserve"> <data name="PropertyOfTypeCannotBeNull" xml:space="preserve">
<value>The '{0}' property of '{1}' must not be null.</value> <value>The '{0}' property of '{1}' must not be null.</value>
</data> </data>

View File

@ -1233,6 +1233,16 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore.InjectedPa
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
} }
[Fact]
public async Task Page_CanOverrideRouteTemplate()
{
// Arrange & Act
var content = await Client.GetStringAsync("like-totally-custom");
// Assert
Assert.Equal("<p>Hey, it's Mr. totally custom here!</p>", content.Trim());
}
private async Task AddAntiforgeryHeaders(HttpRequestMessage request) private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
{ {
var getResponse = await Client.GetAsync(request.RequestUri); var getResponse = await Client.GetAsync(request.RequestUri);

View File

@ -479,23 +479,40 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
} }
[Fact] [Fact]
public void OnProvidersExecuting_ThrowsIfRouteTemplateHasOverridePattern() public void OnProvidersExecuting_AllowsRouteTemplatesWithOverridePattern()
{ {
// Arrange // Arrange
var descriptors = new[] var descriptors = new[]
{ {
CreateVersion_2_0_Descriptor("/Pages/Index.cshtml"), CreateVersion_2_0_Descriptor("/Pages/Index.cshtml", "~/some-other-prefix"),
CreateVersion_2_0_Descriptor("/Pages/Home.cshtml", "/some-prefix"), CreateVersion_2_0_Descriptor("/Pages/Home.cshtml", "/some-prefix"),
}; };
var provider = CreateProvider(descriptors: descriptors); var provider = CreateProvider(descriptors: descriptors);
var context = new PageRouteModelProviderContext(); var context = new PageRouteModelProviderContext();
// Act & Assert // Act
var exception = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context)); provider.OnProvidersExecuting(context);
Assert.Equal(
"The route for the page at '/Pages/Home.cshtml' cannot start with / or ~/. Pages do not support overriding the file path of the page.", // Assert
exception.Message); Assert.Collection(
context.RouteModels,
result =>
{
Assert.Equal("/Pages/Index.cshtml", result.RelativePath);
Assert.Equal("/Index", result.ViewEnginePath);
Assert.Collection(
result.Selectors,
selector => Assert.Equal("some-other-prefix", selector.AttributeRouteModel.Template));
},
result =>
{
Assert.Equal("/Pages/Home.cshtml", result.RelativePath);
Assert.Equal("/Home", result.ViewEnginePath);
Assert.Collection(
result.Selectors,
selector => Assert.Equal("some-prefix", selector.AttributeRouteModel.Template));
});
} }
private TestCompiledPageRouteModelProvider CreateProvider( private TestCompiledPageRouteModelProvider CreateProvider(

View File

@ -67,6 +67,34 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}); });
} }
[Fact]
public void CreateRouteModel_DoesNotAddMultipleSelectorsWhenRouteTemplateIsAbsolute()
{
// Arrange
var relativePath = "/Pages/Users/Profile/Index.cshtml";
var options = new RazorPagesOptions();
var routeModelFactory = new PageRouteModelFactory(options, NullLogger.Instance);
// Act
var routeModel = routeModelFactory.CreateRouteModel(relativePath, "/my-override");
// Assert
Assert.Equal(relativePath, routeModel.RelativePath);
Assert.Equal("/Users/Profile/Index", routeModel.ViewEnginePath);
Assert.Collection(
routeModel.Selectors,
selector => Assert.Equal("my-override", selector.AttributeRouteModel.Template));
Assert.Collection(
routeModel.RouteValues,
kvp =>
{
Assert.Equal("page", kvp.Key);
Assert.Equal("/Users/Profile/Index", kvp.Value);
});
}
[Fact] [Fact]
public void CreateAreaRouteModel_AddsSelector() public void CreateAreaRouteModel_AddsSelector()
{ {

View File

@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
} }
[Fact] [Fact]
public void OnProvidersExecuting_ThrowsIfRouteTemplateHasOverridePattern() public void OnProvidersExecuting_AllowsRouteTemplateWithOverridePattern()
{ {
// Arrange // Arrange
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
@ -296,10 +296,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var provider = new RazorProjectPageRouteModelProvider(project, optionsManager, NullLoggerFactory.Instance); var provider = new RazorProjectPageRouteModelProvider(project, optionsManager, NullLoggerFactory.Instance);
var context = new PageRouteModelProviderContext(); var context = new PageRouteModelProviderContext();
// Act & Assert // Act
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context)); provider.OnProvidersExecuting(context);
Assert.Equal("The route for the page at '/Index.cshtml' cannot start with / or ~/. Pages do not support overriding the file path of the page.",
ex.Message); // Assert
Assert.Collection(
context.RouteModels,
model =>
{
Assert.Equal("/Index.cshtml", model.RelativePath);
Assert.Equal("/Index", model.ViewEnginePath);
Assert.Collection(
model.Selectors,
selector => Assert.Equal("custom-route", selector.AttributeRouteModel.Template));
});
} }
[Fact] [Fact]

View File

@ -0,0 +1,3 @@
@page "~/like-totally-custom"
<p>Hey, it's Mr. totally custom here!</p>