From f878ca5b150c1489e4f25f846d4a17b64a1625fb Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Wed, 15 Apr 2015 15:11:11 -0700 Subject: [PATCH] [Fixes #1919] Added path info to RazorPage exceptions --- .../Properties/Resources.Designer.cs | 44 +++++++++---------- src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs | 13 +++--- src/Microsoft.AspNet.Mvc.Razor/RazorView.cs | 2 +- src/Microsoft.AspNet.Mvc.Razor/Resources.resx | 12 ++--- .../FlushPointTest.cs | 3 +- .../RazorPageTest.cs | 42 +++++++++++++----- .../RazorViewTest.cs | 8 +++- 7 files changed, 75 insertions(+), 49 deletions(-) diff --git a/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs index 8a4691f31e..5236fb203f 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs @@ -91,7 +91,7 @@ namespace Microsoft.AspNet.Mvc.Razor } /// - /// A layout page cannot be rendered after '{0}' has been invoked. + /// Layout page '{0}' cannot be rendered after '{1}' has been invoked. /// internal static string LayoutCannotBeRendered { @@ -99,11 +99,11 @@ namespace Microsoft.AspNet.Mvc.Razor } /// - /// A layout page cannot be rendered after '{0}' has been invoked. + /// Layout page '{0}' cannot be rendered after '{1}' has been invoked. /// - internal static string FormatLayoutCannotBeRendered(object p0) + internal static string FormatLayoutCannotBeRendered(object p0, object p1) { - return string.Format(CultureInfo.CurrentCulture, GetString("LayoutCannotBeRendered"), p0); + return string.Format(CultureInfo.CurrentCulture, GetString("LayoutCannotBeRendered"), p0, p1); } /// @@ -171,19 +171,19 @@ namespace Microsoft.AspNet.Mvc.Razor } /// - /// You cannot flush while inside a writing scope. + /// The {0} operation cannot be performed while inside a writing scope in '{1}'. /// - internal static string RazorPage_YouCannotFlushWhileInAWritingScope + internal static string RazorPage_CannotFlushWhileInAWritingScope { - get { return GetString("RazorPage_YouCannotFlushWhileInAWritingScope"); } + get { return GetString("RazorPage_CannotFlushWhileInAWritingScope"); } } /// - /// You cannot flush while inside a writing scope. + /// The {0} operation cannot be performed while inside a writing scope in '{1}'. /// - internal static string FormatRazorPage_YouCannotFlushWhileInAWritingScope() + internal static string FormatRazorPage_CannotFlushWhileInAWritingScope(object p0, object p1) { - return GetString("RazorPage_YouCannotFlushWhileInAWritingScope"); + return string.Format(CultureInfo.CurrentCulture, GetString("RazorPage_CannotFlushWhileInAWritingScope"), p0, p1); } /// @@ -203,7 +203,7 @@ namespace Microsoft.AspNet.Mvc.Razor } /// - /// {0} can only be called from a layout page. + /// {0} invocation in '{1}' is invalid. {0} can only be called from a layout page. /// internal static string RazorPage_MethodCannotBeCalled { @@ -211,11 +211,11 @@ namespace Microsoft.AspNet.Mvc.Razor } /// - /// {0} can only be called from a layout page. + /// {0} invocation in '{1}' is invalid. {0} can only be called from a layout page. /// - internal static string FormatRazorPage_MethodCannotBeCalled(object p0) + internal static string FormatRazorPage_MethodCannotBeCalled(object p0, object p1) { - return string.Format(CultureInfo.CurrentCulture, GetString("RazorPage_MethodCannotBeCalled"), p0); + return string.Format(CultureInfo.CurrentCulture, GetString("RazorPage_MethodCannotBeCalled"), p0, p1); } /// @@ -251,7 +251,7 @@ namespace Microsoft.AspNet.Mvc.Razor } /// - /// The section named '{0}' has already been rendered. + /// {0} invocation in '{1}' is invalid. The section '{2}' has already been rendered. /// internal static string SectionAlreadyRendered { @@ -259,15 +259,15 @@ namespace Microsoft.AspNet.Mvc.Razor } /// - /// The section named '{0}' has already been rendered. + /// {0} invocation in '{1}' is invalid. The section '{2}' has already been rendered. /// - internal static string FormatSectionAlreadyRendered(object p0) + internal static string FormatSectionAlreadyRendered(object p0, object p1, object p2) { - return string.Format(CultureInfo.CurrentCulture, GetString("SectionAlreadyRendered"), p0); + return string.Format(CultureInfo.CurrentCulture, GetString("SectionAlreadyRendered"), p0, p1, p2); } /// - /// Section '{0}' is not defined. + /// Section '{0}' is not defined in path '{1}'. /// internal static string SectionNotDefined { @@ -275,11 +275,11 @@ namespace Microsoft.AspNet.Mvc.Razor } /// - /// Section '{0}' is not defined. + /// Section '{0}' is not defined in path '{1}'. /// - internal static string FormatSectionNotDefined(object p0) + internal static string FormatSectionNotDefined(object p0, object p1) { - return string.Format(CultureInfo.CurrentCulture, GetString("SectionNotDefined"), p0); + return string.Format(CultureInfo.CurrentCulture, GetString("SectionNotDefined"), p0, p1); } /// diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs index 6fbb8185fd..5d85e48c37 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorPage.cs @@ -608,7 +608,7 @@ namespace Microsoft.AspNet.Mvc.Razor { if (RenderBodyDelegate == null) { - var message = Resources.FormatRazorPage_MethodCannotBeCalled(nameof(RenderBody)); + var message = Resources.FormatRazorPage_MethodCannotBeCalled(nameof(RenderBody), Path); throw new InvalidOperationException(message); } @@ -704,7 +704,7 @@ namespace Microsoft.AspNet.Mvc.Razor { if (_renderedSections.Contains(sectionName)) { - var message = Resources.FormatSectionAlreadyRendered(sectionName); + var message = Resources.FormatSectionAlreadyRendered(nameof(RenderSectionAsync), Path, sectionName); throw new InvalidOperationException(message); } @@ -721,7 +721,7 @@ namespace Microsoft.AspNet.Mvc.Razor else if (required) { // If the section is not found, and it is not optional, throw an error. - throw new InvalidOperationException(Resources.FormatSectionNotDefined(sectionName)); + throw new InvalidOperationException(Resources.FormatSectionNotDefined(sectionName, Path)); } else { @@ -748,14 +748,15 @@ namespace Microsoft.AspNet.Mvc.Razor // change. if (_writerScopes.Count > 0) { - throw new InvalidOperationException(Resources.RazorPage_YouCannotFlushWhileInAWritingScope); + throw new InvalidOperationException( + Resources.FormatRazorPage_CannotFlushWhileInAWritingScope(nameof(FlushAsync), Path)); } // Calls to Flush are allowed if the page does not specify a Layout or if it is executing a section in the // Layout. if (!IsLayoutBeingRendered && !string.IsNullOrEmpty(Layout)) { - var message = Resources.FormatLayoutCannotBeRendered(nameof(FlushAsync)); + var message = Resources.FormatLayoutCannotBeRendered(Path, nameof(FlushAsync)); throw new InvalidOperationException(message); } @@ -817,7 +818,7 @@ namespace Microsoft.AspNet.Mvc.Razor { if (PreviousSectionWriters == null) { - throw new InvalidOperationException(Resources.FormatRazorPage_MethodCannotBeCalled(methodName)); + throw new InvalidOperationException(Resources.FormatRazorPage_MethodCannotBeCalled(methodName, Path)); } } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs index 7a574b2093..f332cb5f49 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorView.cs @@ -179,7 +179,7 @@ namespace Microsoft.AspNet.Mvc.Razor // the body content. Throwing this exception wouldn't return a 500 (since content has already been // written), but a diagnostic component should be able to capture it. - var message = Resources.FormatLayoutCannotBeRendered("FlushAsync"); + var message = Resources.FormatLayoutCannotBeRendered(Path, nameof(Razor.RazorPage.FlushAsync)); throw new InvalidOperationException(message); } diff --git a/src/Microsoft.AspNet.Mvc.Razor/Resources.resx b/src/Microsoft.AspNet.Mvc.Razor/Resources.resx index 101065801c..0d39f11535 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.Razor/Resources.resx @@ -133,7 +133,7 @@ The layout view '{0}' could not be located. The following locations were searched:{1} - A layout page cannot be rendered after '{0}' has been invoked. + Layout page '{0}' cannot be rendered after '{1}' has been invoked. The 'inherits' keyword is not allowed when a '{0}' keyword is used. @@ -147,14 +147,14 @@ There is no active writing scope to end. - - You cannot flush while inside a writing scope. + + The {0} operation cannot be performed while inside a writing scope in '{1}'. The {0} was unable to provide metadata for expression '{1}'. - {0} can only be called from a layout page. + {0} invocation in '{1}' is invalid. {0} can only be called from a layout page. {0} has not been called for the page at '{1}'. @@ -163,10 +163,10 @@ Section '{0}' is already defined. - The section named '{0}' has already been rendered. + {0} invocation in '{1}' is invalid. The section '{2}' has already been rendered. - Section '{0}' is not defined. + Section '{0}' is not defined in path '{1}'. The following sections have been defined but have not been rendered by the page at '{0}': '{1}'. diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/FlushPointTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/FlushPointTest.cs index bd2f0c53a1..fcefba7091 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/FlushPointTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/FlushPointTest.cs @@ -165,7 +165,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests }); var client = server.CreateClient(); - var expectedMessage = "A layout page cannot be rendered after 'FlushAsync' has been invoked."; + var expectedMessage = "Layout page '/Views/FlushPoint/PageWithFlushBeforeLayout.cshtml'" + + " cannot be rendered after 'FlushAsync' has been invoked."; // Act var stream = await client.GetStreamAsync("http://localhost/FlushPoint/PageWithFlushBeforeLayout"); diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageTest.cs index ba1bbe8f82..a21082d863 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorPageTest.cs @@ -107,6 +107,7 @@ namespace Microsoft.AspNet.Mvc.Razor var viewContext = CreateViewContext(); var page = CreatePage(async v => { + v.Path = "/Views/TestPath/Test.cshtml"; v.StartTagHelperWritingScope(); await v.FlushAsync(); }); @@ -116,7 +117,8 @@ namespace Microsoft.AspNet.Mvc.Razor () => page.ExecuteAsync()); // Assert - Assert.Equal("You cannot flush while inside a writing scope.", ex.Message); + Assert.Equal("The FlushAsync operation cannot be performed while " + + "inside a writing scope in '/Views/TestPath/Test.cshtml'.", ex.Message); } [Fact] @@ -229,6 +231,7 @@ namespace Microsoft.AspNet.Mvc.Razor Exception ex = null; var page = CreatePage(v => { + v.Path = "/Views/TestPath/Test.cshtml"; ex = Assert.Throws(() => v.RenderSection("bar")); }); @@ -236,8 +239,9 @@ namespace Microsoft.AspNet.Mvc.Razor await page.ExecuteAsync(); // Assert - Assert.Equal("RenderSection can only be called from a layout page.", - ex.Message); + Assert.Equal("RenderSection invocation in '/Views/TestPath/Test.cshtml' is invalid. " + + "RenderSection can only be called from a layout page.", + ex.Message); } [Fact] @@ -246,6 +250,7 @@ namespace Microsoft.AspNet.Mvc.Razor // Arrange var page = CreatePage(v => { + v.Path = "/Views/TestPath/Test.cshtml"; v.RenderSection("bar"); }); page.PreviousSectionWriters = new Dictionary @@ -257,18 +262,23 @@ namespace Microsoft.AspNet.Mvc.Razor var ex = await Assert.ThrowsAsync(() => page.ExecuteAsync()); // Assert - Assert.Equal("Section 'bar' is not defined.", ex.Message); + Assert.Equal("Section 'bar' is not defined in path '/Views/TestPath/Test.cshtml'.", ex.Message); } [Fact] public void IsSectionDefined_ThrowsIfPreviousSectionWritersIsNotRegistered() { // Arrange - var page = CreatePage(v => { }); + var page = CreatePage(v => + { + v.Path = "/Views/TestPath/Test.cshtml"; + }); // Act and Assert + page.ExecuteAsync(); ExceptionAssert.Throws(() => page.IsSectionDefined("foo"), - "IsSectionDefined can only be called from a layout page."); + "IsSectionDefined invocation in '/Views/TestPath/Test.cshtml' is invalid." + + " IsSectionDefined can only be called from a layout page."); } [Fact] @@ -326,6 +336,7 @@ namespace Microsoft.AspNet.Mvc.Razor var expected = new HelperResult(action: null); var page = CreatePage(v => { + v.Path = "/Views/TestPath/Test.cshtml"; v.RenderSection("header"); v.RenderSection("header"); }); @@ -338,7 +349,8 @@ namespace Microsoft.AspNet.Mvc.Razor var ex = await Assert.ThrowsAsync(page.ExecuteAsync); // Assert - Assert.Equal("The section named 'header' has already been rendered.", ex.Message); + Assert.Equal("RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." + + " The section 'header' has already been rendered.", ex.Message); } [Fact] @@ -348,6 +360,7 @@ namespace Microsoft.AspNet.Mvc.Razor var expected = new HelperResult(action: null); var page = CreatePage(async v => { + v.Path = "/Views/TestPath/Test.cshtml"; await v.RenderSectionAsync("header"); await v.RenderSectionAsync("header"); }); @@ -360,7 +373,8 @@ namespace Microsoft.AspNet.Mvc.Razor var ex = await Assert.ThrowsAsync(page.ExecuteAsync); // Assert - Assert.Equal("The section named 'header' has already been rendered.", ex.Message); + Assert.Equal("RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." + + " The section 'header' has already been rendered.", ex.Message); } [Fact] @@ -370,6 +384,7 @@ namespace Microsoft.AspNet.Mvc.Razor var expected = new HelperResult(action: null); var page = CreatePage(async v => { + v.Path = "/Views/TestPath/Test.cshtml"; v.RenderSection("header"); await v.RenderSectionAsync("header"); }); @@ -382,7 +397,8 @@ namespace Microsoft.AspNet.Mvc.Razor var ex = await Assert.ThrowsAsync(page.ExecuteAsync); // Assert - Assert.Equal("The section named 'header' has already been rendered.", ex.Message); + Assert.Equal("RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid." + + " The section 'header' has already been rendered.", ex.Message); } [Fact] @@ -392,6 +408,7 @@ namespace Microsoft.AspNet.Mvc.Razor var expected = new HelperResult(action: null); var page = CreatePage(async v => { + v.Path = "/Views/TestPath/Test.cshtml"; await v.RenderSectionAsync("header"); }); @@ -399,7 +416,8 @@ namespace Microsoft.AspNet.Mvc.Razor var ex = await Assert.ThrowsAsync(page.ExecuteAsync); // Assert - Assert.Equal("RenderSectionAsync can only be called from a layout page.", ex.Message); + Assert.Equal("RenderSectionAsync invocation in '/Views/TestPath/Test.cshtml' is invalid. " + + "RenderSectionAsync can only be called from a layout page.", ex.Message); } [Fact] @@ -566,11 +584,13 @@ namespace Microsoft.AspNet.Mvc.Razor public async Task FlushAsync_ThrowsIfTheLayoutHasBeenSet() { // Arrange - var expected = @"A layout page cannot be rendered after 'FlushAsync' has been invoked."; + var expected = "Layout page '/Views/TestPath/Test.cshtml' cannot be rendered" + + " after 'FlushAsync' has been invoked."; var writer = new Mock(); var context = CreateViewContext(writer.Object); var page = CreatePage(async p => { + p.Path = "/Views/TestPath/Test.cshtml"; p.Layout = "foo"; await p.FlushAsync(); }, context); diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs index daeab6bc39..2b0cede161 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewTest.cs @@ -961,9 +961,11 @@ namespace Microsoft.AspNet.Mvc.Razor public async Task RenderAsync_ThrowsIfLayoutIsSpecifiedWhenNotBuffered() { // Arrange - var expected = @"A layout page cannot be rendered after 'FlushAsync' has been invoked."; + var expected = "Layout page '/Views/TestPath/Test.cshtml' cannot be rendered" + + " after 'FlushAsync' has been invoked."; var page = new TestableRazorPage(v => { + v.Path = "/Views/TestPath/Test.cshtml"; v.WriteLiteral("before-flush" + Environment.NewLine); v.FlushAsync().Wait(); v.Layout = "test-layout"; @@ -986,9 +988,11 @@ namespace Microsoft.AspNet.Mvc.Razor public async Task RenderAsync_ThrowsIfFlushWasInvokedInsideRenderedSectionAndLayoutWasSet() { // Arrange - var expected = @"A layout page cannot be rendered after 'FlushAsync' has been invoked."; + var expected = "Layout page '/Views/TestPath/Test.cshtml' cannot be rendered" + + " after 'FlushAsync' has been invoked."; var page = new TestableRazorPage(v => { + v.Path = "/Views/TestPath/Test.cshtml"; v.HtmlEncoder = new HtmlEncoder(); v.DefineSection("foo", async writer => {