diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs index 85a04cb3aa..1d3e34e3a5 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs @@ -3,12 +3,29 @@ using System; using System.IO; +using Microsoft.AspNetCore.Mvc.Razor.Extensions; using Microsoft.AspNetCore.Razor.Language; namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure { public static class PageDirectiveFeature { + private static readonly RazorEngine PageDirectiveEngine = RazorEngine.Create(builder => + { + for (var i = builder.Phases.Count - 1; i >= 0; i--) + { + var phase = builder.Phases[i]; + builder.Phases.RemoveAt(i); + if (phase is IRazorDocumentClassifierPhase) + { + break; + } + } + + RazorExtensions.Register(builder); + builder.Features.Add(new PageDirectiveParserOptionsFeature()); + }); + public static bool TryGetPageDirective(RazorProjectItem projectItem, out string template) { if (projectItem == null) @@ -16,7 +33,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure throw new ArgumentNullException(nameof(projectItem)); } - return TryGetPageDirective(projectItem.Read, out template); + var sourceDocument = RazorSourceDocument.ReadFrom(projectItem); + return TryGetPageDirective(sourceDocument, out template); } public static bool TryGetPageDirective(Func streamFactory, out string template) @@ -26,38 +44,36 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure throw new ArgumentNullException(nameof(streamFactory)); } - const string PageDirective = "@page"; - - string content; - using (var streamReader = new StreamReader(streamFactory())) + using (var stream = streamFactory()) { - do - { - content = streamReader.ReadLine(); + var sourceDocument = RazorSourceDocument.ReadFrom(stream, fileName: "Parse.cshtml"); + return TryGetPageDirective(sourceDocument, out template); + } + } - } while (content != null && string.IsNullOrWhiteSpace(content)); - content = content?.Trim(); + private static bool TryGetPageDirective(RazorSourceDocument sourceDocument, out string template) + { + var codeDocument = RazorCodeDocument.Create(sourceDocument); + PageDirectiveEngine.Process(codeDocument); + + if (PageDirective.TryGetPageDirective(codeDocument.GetIRDocument(), out var pageDirective)) + { + template = pageDirective.RouteTemplate; + return true; } - if (content == null || !content.StartsWith(PageDirective, StringComparison.Ordinal)) - { - template = null; - return false; - } + template = null; + return false; + } - template = content.Substring(PageDirective.Length, content.Length - PageDirective.Length).TrimStart(); + private class PageDirectiveParserOptionsFeature : RazorEngineFeatureBase, IRazorParserOptionsFeature + { + public int Order { get; } - if (template.StartsWith("\"") && template.EndsWith("\"")) + public void Configure(RazorParserOptionsBuilder options) { - template = template.Substring(1, template.Length - 2); + options.ParseOnlyLeadingDirectives = true; } - // If it's not in quotes it's not our template - else - { - template = string.Empty; - } - - return true; } } } diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs index ed86b5346b..18d8b8c8f0 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs @@ -48,18 +48,6 @@ The rest of the thing"); Assert.Null(template); } - [Fact] - public void TryGetPageDirective_MultipleQuotes() - { - // Arrange - string template; - var projectItem = new TestRazorProjectItem(@"@page """"template"""""); - - // Act & Assert - Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); - Assert.Equal(@"""template""", template); - } - [Theory] [InlineData(@"""Some/Path/{value}")] [InlineData(@"Some/Path/{value}""")] @@ -71,10 +59,9 @@ The rest of the thing"); // Act & Assert Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); - Assert.Equal(string.Empty, template); + Assert.Null(template); } - [Fact] public void TryGetPageDirective_NoQuotesAroundPath_IsNotTemplate() { @@ -84,7 +71,7 @@ The rest of the thing"); // Act & Assert Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); - Assert.Equal(string.Empty, template); + Assert.Null(template); } [Fact] @@ -126,7 +113,7 @@ The rest of the thing"); Assert.Equal("Some/Path/{value}", template); } - [Fact(Skip = "Re-evaluate this scenario after we use Razor to parse this stuff")] + [Fact] public void TryGetPageDirective_JunkBeforeNewline() { // Arrange @@ -136,7 +123,7 @@ a new line"); // Act & Assert Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); - Assert.Empty(template); + Assert.Equal("Some/Path/{value}", template); } [Fact] @@ -148,7 +135,7 @@ a new line"); // Act & Assert Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); - Assert.Empty(template); + Assert.Null(template); } [Fact] @@ -161,7 +148,7 @@ Non-path things"); // Act & Assert Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); - Assert.Empty(template); + Assert.Null(template); } [Fact] @@ -236,21 +223,9 @@ Nobody will use it"); } } - public override string Path - { - get - { - throw new NotImplementedException(); - } - } + public override string Path => "Test.cshtml"; - public override string PhysicalPath - { - get - { - throw new NotImplementedException(); - } - } + public override string PhysicalPath => null; public override Stream Read() {