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