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);
if (AttributeRouteModel.IsOverridePattern(routeTemplate))
{
throw new InvalidOperationException(string.Format(
Resources.PageActionDescriptorProvider_RouteTemplateCannotBeOverrideable,
model.RelativePath));
}
var selectorModel = CreateSelectorModel(pageRoute, routeTemplate);
model.Selectors.Add(selectorModel);
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
// force outgoing routes to match to the path sans /Index.
// For pages without an override route, and ending in /Index.cshtml, we want to allow
// incoming routing, but force outgoing routes to match to the path sans /Index.
selectorModel.AttributeRouteModel.SuppressLinkGeneration = true;
var index = pageRoute.LastIndexOf('/');

View File

@ -117,9 +117,6 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</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">
<value>The '{0}' property of '{1}' must not be null.</value>
</data>

View File

@ -1233,6 +1233,16 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore.InjectedPa
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)
{
var getResponse = await Client.GetAsync(request.RequestUri);

View File

@ -479,23 +479,40 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}
[Fact]
public void OnProvidersExecuting_ThrowsIfRouteTemplateHasOverridePattern()
public void OnProvidersExecuting_AllowsRouteTemplatesWithOverridePattern()
{
// Arrange
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"),
};
var provider = CreateProvider(descriptors: descriptors);
var context = new PageRouteModelProviderContext();
// Act & Assert
var exception = Assert.Throws<InvalidOperationException>(() => 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.",
exception.Message);
// Act
provider.OnProvidersExecuting(context);
// Assert
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(

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]
public void CreateAreaRouteModel_AddsSelector()
{

View File

@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}
[Fact]
public void OnProvidersExecuting_ThrowsIfRouteTemplateHasOverridePattern()
public void OnProvidersExecuting_AllowsRouteTemplateWithOverridePattern()
{
// Arrange
var fileProvider = new TestFileProvider();
@ -296,10 +296,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var provider = new RazorProjectPageRouteModelProvider(project, optionsManager, NullLoggerFactory.Instance);
var context = new PageRouteModelProviderContext();
// Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => 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);
// Act
provider.OnProvidersExecuting(context);
// 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]

View File

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