CompiledPageRouteModelProvider should de-dup descriptors

Fixes #7543
This commit is contained in:
Pranav K 2018-03-26 17:03:50 -07:00
parent de3f77c34b
commit f8e315d03d
No known key found for this signature in database
GPG Key ID: 1963DA6D96C3057A
8 changed files with 138 additions and 9 deletions

View File

@ -58,22 +58,55 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
}
}
/// <summary>
/// Gets the sequence of <see cref="CompiledViewDescriptor"/> from <paramref name="applicationManager"/>.
/// </summary>
/// <param name="applicationManager">The <see cref="ApplicationPartManager"/>s</param>
/// <returns>The sequence of <see cref="CompiledViewDescriptor"/>.</returns>
protected virtual IEnumerable<CompiledViewDescriptor> GetViewDescriptors(ApplicationPartManager applicationManager)
private IEnumerable<CompiledViewDescriptor> GetViewDescriptors(ApplicationPartManager applicationManager)
{
if (applicationManager == null)
{
throw new ArgumentNullException(nameof(applicationManager));
}
var viewsFeature = GetViewFeature(applicationManager);
var visited = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var viewDescriptor in viewsFeature.ViewDescriptors)
{
if (!visited.Add(viewDescriptor.RelativePath))
{
// Already seen an descriptor with a higher "order"
continue;
}
if (!viewDescriptor.IsPrecompiled)
{
continue;
}
if (IsRazorPage(viewDescriptor))
{
yield return viewDescriptor;
}
}
bool IsRazorPage(CompiledViewDescriptor viewDescriptor)
{
if (viewDescriptor.Item != null)
{
return viewDescriptor.Item.Kind == RazorPageDocumentClassifierPass.RazorPageDocumentKind;
}
else if (viewDescriptor.ViewAttribute != null)
{
return viewDescriptor.ViewAttribute is RazorPageAttribute;
}
return false;
}
}
protected virtual ViewsFeature GetViewFeature(ApplicationPartManager applicationManager)
{
var viewsFeature = new ViewsFeature();
applicationManager.PopulateFeature(viewsFeature);
return viewsFeature.ViewDescriptors.Where(d => d.IsPrecompiled && d.ViewAttribute is RazorPageAttribute);
return viewsFeature;
}
private void CreateModels(PageRouteModelProviderContext context)

View File

@ -484,5 +484,25 @@ Hello from /Pages/Shared/";
// Assert
Assert.Contains("Name is required", response);
}
[Fact]
public async Task PagesFromClassLibraries_CanBeServed()
{
// Act
var response = await Client.GetStringAsync("/ClassLibraryPages/Served");
// Assert
Assert.Contains("This page is served from RazorPagesClassLibrary", response);
}
[Fact]
public async Task PagesFromClassLibraries_CanBeOverriden()
{
// Act
var response = await Client.GetStringAsync("/ClassLibraryPages/Overriden");
// Assert
Assert.Contains("This page is overriden by RazorPagesWebSite", response);
}
}
}

View File

@ -518,6 +518,53 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
});
}
[Fact]
public void OnProvidersExecuting_UsesTheFirstDescriptorForEachPath()
{
// ViewsFeature may contain duplicate entries for the same Page - for instance when an app overloads a library's views.
// It picks the first entry for each path. In the ordinary case, this should ensure that the app's Razor Pages are prefered
// to a Razor Page added by a library.
// Arrange
var descriptors = new[]
{
// Page coming from the app
CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[]
{
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"),
}),
CreateVersion_2_1_Descriptor("/Pages/Home.cshtml", metadata: new object[]
{
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/Index.cshtml"),
}),
// Page coming from the app
CreateVersion_2_1_Descriptor("/Pages/About.cshtml", metadata: new object[]
{
new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), "/Pages/About.cshtml"),
}),
};
var provider = CreateProvider(descriptors: descriptors);
var context = new PageRouteModelProviderContext();
// Act
provider.OnProvidersExecuting(context);
// Assert
Assert.Collection(
context.RouteModels,
result =>
{
Assert.Equal("/Pages/About.cshtml", result.RelativePath);
Assert.Equal("/About", result.ViewEnginePath);
},
result =>
{
Assert.Equal("/Pages/Home.cshtml", result.RelativePath);
Assert.Equal("/Home", result.ViewEnginePath);
});
}
[Fact]
public void GetRouteTemplate_ReturnsPathFromRazorPageAttribute()
{
@ -602,6 +649,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
return new CompiledViewDescriptor
{
IsPrecompiled = true,
RelativePath = path,
ViewAttribute = new RazorPageAttribute(path, typeof(object), routeTemplate),
};
@ -613,6 +661,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
return new CompiledViewDescriptor
{
IsPrecompiled = true,
RelativePath = path,
Item = new TestRazorCompiledItem(typeof(object), "mvc.1.0.razor-page", path, metadata ?? Array.Empty<object>()),
};
@ -631,7 +680,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public List<CompiledViewDescriptor> Descriptors { get; } = new List<CompiledViewDescriptor>();
protected override IEnumerable<CompiledViewDescriptor> GetViewDescriptors(ApplicationPartManager applicationManager) => Descriptors;
protected override ViewsFeature GetViewFeature(ApplicationPartManager applicationManager)
{
var feature = new ViewsFeature();
foreach (var descriptor in Descriptors)
{
feature.ViewDescriptors.Add(descriptor);
}
return feature;
}
}
}
}

View File

@ -0,0 +1,2 @@
@page
@{ throw new Exception("This page should be overriden by the application."); }

View File

@ -0,0 +1,2 @@
@page
This page is served from RazorPagesClassLibrary

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,2 @@
@page
This page is overriden by RazorPagesWebSite

View File

@ -6,6 +6,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\RazorPagesClassLibrary\RazorPagesClassLibrary.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="$(MicrosoftAspNetCoreAuthenticationCookiesPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />