From 85e28ae478164431904f4ac2c414c77258a35cf6 Mon Sep 17 00:00:00 2001 From: Ryan Brandenburg Date: Fri, 3 Mar 2017 09:52:36 -0800 Subject: [PATCH] Trim quotes from template (#5838) --- .../PageActionDescriptorProvider.cs | 2 +- .../Infrastructure/PageDirectiveFeature.cs | 41 ++- .../RazorPagesTest.cs | 34 ++- .../PageActionDescriptorProviderTest.cs | 6 +- .../PageDirectiveFeatureTest.cs | 267 ++++++++++++++++++ .../HelloWorldWithRoute.cshtml | 2 +- test/WebSites/RazorPagesWebSite/NoPage.cshtml | 1 + .../PageWithoutContent.cshtml | 1 + 8 files changed, 336 insertions(+), 18 deletions(-) create mode 100644 test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs create mode 100644 test/WebSites/RazorPagesWebSite/NoPage.cshtml create mode 100644 test/WebSites/RazorPagesWebSite/PageWithoutContent.cshtml diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs index b9333afe6c..f367926d83 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageActionDescriptorProvider.cs @@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure } string template; - if (!PageDirectiveFeature.TryGetRouteTemplate(item, out template)) + if (!PageDirectiveFeature.TryGetPageDirective(item, out template)) { // .cshtml pages without @page are not RazorPages. continue; diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs index 64dc2c0e9b..31045c832e 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/PageDirectiveFeature.cs @@ -9,25 +9,46 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure { public static class PageDirectiveFeature { - public static bool TryGetRouteTemplate(RazorProjectItem projectItem, out string template) + public static bool TryGetPageDirective(RazorProjectItem projectItem, out string template) { + if (projectItem == null) + { + throw new ArgumentNullException(nameof(projectItem)); + } + const string PageDirective = "@page"; - string content; - using (var streamReader = new StreamReader(projectItem.Read())) + var stream = projectItem.Read(); + + string content = null; + using (var streamReader = new StreamReader(stream)) { - content = streamReader.ReadToEnd(); + do + { + content = streamReader.ReadLine(); + } while (content != null && string.IsNullOrWhiteSpace(content)); + content = content?.Trim(); } - if (content.StartsWith(PageDirective, StringComparison.Ordinal)) + if (content == null || !content.StartsWith(PageDirective, StringComparison.Ordinal)) { - var newLineIndex = content.IndexOf(Environment.NewLine, PageDirective.Length); - template = content.Substring(PageDirective.Length, newLineIndex - PageDirective.Length).Trim(); - return true; + template = null; + return false; } - template = null; - return false; + template = content.Substring(PageDirective.Length, content.Length - PageDirective.Length).TrimStart(); + + if (template.StartsWith("\"") && template.EndsWith("\"")) + { + template = template.Substring(1, template.Length - 2); + } + // If it's not in quotes it's not our template + else + { + template = string.Empty; + } + + return true; } } } diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs index 1420b1a1c6..35feac6179 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs @@ -1,7 +1,6 @@ // 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.Linq; using System.Net; using System.Net.Http; @@ -19,6 +18,19 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public HttpClient Client { get; } + [Fact] + public async Task NoPage_NotFound() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/NoPage"); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); + } + [Fact] public async Task HelloWorld_CanGetContent() { @@ -83,16 +95,32 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests Assert.Equal("Hello, pagemodel!", content.Trim()); } + [Fact] + public async Task PageWithoutContent() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/PageWithoutContent/No/Content/Path"); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var content = await response.Content.ReadAsStringAsync(); + Assert.Equal("", content); + } + [Fact] public async Task TempData_SetTempDataInPage_CanReadValue() { // Arrange 1 var url = "http://localhost/TempData/SetTempDataOnPageAndRedirect?message=Hi1"; var request = new HttpRequestMessage(HttpMethod.Get, url); - + // Act 1 var response = await Client.SendAsync(request); - + // Assert 1 Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs index dc3f7055e5..a87c14f25f 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageActionDescriptorProviderTest.cs @@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure razorProject.Setup(p => p.EnumerateItems("/")) .Returns(new[] { - GetProjectItem("/", "/Test.cshtml", $"@page Home {Environment.NewLine}

Hello world

"), + GetProjectItem("/", "/Test.cshtml", $"@page \"Home\" {Environment.NewLine}

Hello world

"), }); var provider = new PageActionDescriptorProvider( razorProject.Object, @@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure razorProject.Setup(p => p.EnumerateItems("/")) .Returns(new[] { - GetProjectItem("/", "/Test.cshtml", $"@page {template} {Environment.NewLine}

Hello world

"), + GetProjectItem("/", "/Test.cshtml", $"@page \"{template}\" {Environment.NewLine}

Hello world

"), }); var provider = new PageActionDescriptorProvider( razorProject.Object, @@ -166,7 +166,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure razorProject.Setup(p => p.EnumerateItems("/")) .Returns(new[] { - GetProjectItem("", "/Catalog/Details/Index.cshtml", $"@page {{id:int?}} {Environment.NewLine}"), + GetProjectItem("", "/Catalog/Details/Index.cshtml", $"@page \"{{id:int?}}\" {Environment.NewLine}"), }); var provider = new PageActionDescriptorProvider( razorProject.Object, diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs new file mode 100644 index 0000000000..4761823348 --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Infrastructure/PageDirectiveFeatureTest.cs @@ -0,0 +1,267 @@ +// 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.AspNetCore.Razor.Evolution; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure +{ + public class PageDirectiveFeatureTest + { + [Fact] + public void TryGetPageDirective_FindsTemplate() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@"@page ""Some/Path/{value}"" +The rest of the thing"); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Equal("Some/Path/{value}", template); + } + + [Fact] + public void TryGetPageDirective_NoNewLine() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@"@page ""Some/Path/{value}"""); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Equal("Some/Path/{value}", template); + } + + [Fact] + public void TryGetPageDirective_JunkBeforeDirective() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@"Not a directive @page ""Some/Path/{value}"""); + + // Act & Assert + Assert.False(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + 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}""")] + public void TryGetPageDirective_RequiresBothQuotes(string inTemplate) + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem($@"@page {inTemplate}"); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Equal(string.Empty, template); + } + + + [Fact] + public void TryGetPageDirective_NoQuotesAroundPath_IsNotTemplate() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@"@page Some/Path/{value}"); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Equal(string.Empty, template); + } + + [Fact] + public void TryGetPageDirective_WrongNewLine() + { + // Arrange + var wrongNewLine = Environment.NewLine == "\r\n" ? "\n" : "\r\n"; + + string template; + var projectItem = new TestRazorProjectItem($"@page \"Some/Path/{{value}}\" {wrongNewLine}"); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Equal("Some/Path/{value}", template); + } + + [Fact] + public void TryGetPageDirective_NewLineBeforeDirective() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem("\n @page \"Some/Path/{value}\""); + + // Act + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Equal("Some/Path/{value}", template); + } + + [Fact] + public void TryGetPageDirective_WhitespaceBeforeDirective() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@" @page ""Some/Path/{value}"" +"); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Equal("Some/Path/{value}", template); + } + + [Fact(Skip = "Re-evaluate this scenario after we use Razor to parse this stuff")] + public void TryGetPageDirective_JunkBeforeNewline() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@"@page ""Some/Path/{value}"" things that are not the path +a new line"); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Empty(template); + } + + [Fact] + public void TryGetPageDirective_Directive_WithoutPathOrContent() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@"@page"); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Empty(template); + } + + [Fact] + public void TryGetPageDirective_DirectiveWithContent_WithoutPath() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@"@page +Non-path things"); + + // Act & Assert + Assert.True(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Empty(template); + } + + [Fact] + public void TryGetPageDirective_NoDirective() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(@"This is junk +Nobody will use it"); + + // Act & Assert + Assert.False(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Null(template); + } + + [Fact] + public void TryGetPageDirective_EmptyStream() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(string.Empty); + + // Act + Assert.False(PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + Assert.Null(template); + } + + [Fact] + public void TryGetPageDirective_NullProject() + { + // Arrange + string template; + + // Act & Assert + Assert.Throws(() => PageDirectiveFeature.TryGetPageDirective(projectItem: null, template: out template)); + } + + [Fact] + public void TryGetPageDirective_NullStream() + { + // Arrange + string template; + var projectItem = new TestRazorProjectItem(content: null); + + // Act & Assert + Assert.Throws(() => PageDirectiveFeature.TryGetPageDirective(projectItem, out template)); + } + } + + public class TestRazorProjectItem : RazorProjectItem + { + private string _content; + + public TestRazorProjectItem(string content) + { + _content = content; + } + + public override string BasePath + { + get + { + throw new NotImplementedException(); + } + } + + public override bool Exists + { + get + { + throw new NotImplementedException(); + } + } + + public override string Path + { + get + { + throw new NotImplementedException(); + } + } + + public override string PhysicalPath + { + get + { + throw new NotImplementedException(); + } + } + + public override Stream Read() + { + if (_content == null) + { + return null; + } + else + { + return new MemoryStream(Encoding.UTF8.GetBytes(_content)); + } + } + } +} diff --git a/test/WebSites/RazorPagesWebSite/HelloWorldWithRoute.cshtml b/test/WebSites/RazorPagesWebSite/HelloWorldWithRoute.cshtml index 419f760dd5..9a2c6012cc 100644 --- a/test/WebSites/RazorPagesWebSite/HelloWorldWithRoute.cshtml +++ b/test/WebSites/RazorPagesWebSite/HelloWorldWithRoute.cshtml @@ -1,3 +1,3 @@ -@page Some/Path/{text} +@page "Some/Path/{text}" Hello, @(ViewContext.RouteData.Values["text"])! \ No newline at end of file diff --git a/test/WebSites/RazorPagesWebSite/NoPage.cshtml b/test/WebSites/RazorPagesWebSite/NoPage.cshtml new file mode 100644 index 0000000000..a9bfcd99bc --- /dev/null +++ b/test/WebSites/RazorPagesWebSite/NoPage.cshtml @@ -0,0 +1 @@ +This isn't a razor page \ No newline at end of file diff --git a/test/WebSites/RazorPagesWebSite/PageWithoutContent.cshtml b/test/WebSites/RazorPagesWebSite/PageWithoutContent.cshtml new file mode 100644 index 0000000000..95c975eff3 --- /dev/null +++ b/test/WebSites/RazorPagesWebSite/PageWithoutContent.cshtml @@ -0,0 +1 @@ +@page "No/Content/Path" \ No newline at end of file