parent
9299565706
commit
7667eba34e
|
|
@ -62,32 +62,10 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
_pageExecutionFeature = context.HttpContext.GetFeature<IPageExecutionListenerFeature>();
|
||||
|
||||
if (IsPartial)
|
||||
{
|
||||
await RenderPartialAsync(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bodyWriter = await RenderPageAsync(RazorPage, context, executeViewStart: true);
|
||||
await RenderLayoutAsync(context, bodyWriter);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RenderPartialAsync(ViewContext context)
|
||||
{
|
||||
if (EnableInstrumentation)
|
||||
{
|
||||
// When instrmenting, we need to Decorate the output in an instrumented writer which
|
||||
// RenderPageAsync does.
|
||||
var bodyWriter = await RenderPageAsync(RazorPage, context, executeViewStart: false);
|
||||
await bodyWriter.CopyToAsync(context.Writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For the non-instrumented case, we don't need to buffer contents. For Html.Partial, the writer is
|
||||
// an in memory writer and for Partial views, we directly write to the Response.
|
||||
await RenderPageCoreAsync(RazorPage, context);
|
||||
}
|
||||
// Partials don't execute _ViewStart pages, but may execute Layout pages if the Layout property
|
||||
// is explicitly specified in the page.
|
||||
var bodyWriter = await RenderPageAsync(RazorPage, context, executeViewStart: !IsPartial);
|
||||
await RenderLayoutAsync(context, bodyWriter);
|
||||
}
|
||||
|
||||
private async Task<IBufferedTextWriter> RenderPageAsync(IRazorPage page,
|
||||
|
|
|
|||
|
|
@ -164,25 +164,41 @@ component-content";
|
|||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> PartialRazorViews_DoNotRenderLayoutData
|
||||
public static IEnumerable<object[]> RazorViewEngine_RendersPartialViewsData
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new[]
|
||||
{
|
||||
"ViewWithoutLayout", @"ViewWithoutLayout-Content"
|
||||
"ViewWithoutLayout", "ViewWithoutLayout-Content"
|
||||
};
|
||||
yield return new[]
|
||||
{
|
||||
"PartialViewWithNamePassedIn", @"ViewWithLayout-Content"
|
||||
"PartialViewWithNamePassedIn",
|
||||
@"<layout>
|
||||
|
||||
ViewWithLayout-Content
|
||||
</layout>"
|
||||
};
|
||||
yield return new[]
|
||||
{
|
||||
"ViewWithFullPath", "ViewWithFullPath-content"
|
||||
"ViewWithFullPath",
|
||||
@"<layout>
|
||||
|
||||
ViewWithFullPath-content
|
||||
</layout>"
|
||||
};
|
||||
yield return new[]
|
||||
{
|
||||
"ViewWithNestedLayout", "ViewWithNestedLayout-Content"
|
||||
"ViewWithNestedLayout",
|
||||
@"<layout>
|
||||
|
||||
<nested-layout>
|
||||
/PartialViewEngine/ViewWithNestedLayout
|
||||
|
||||
ViewWithNestedLayout-Content
|
||||
</nested-layout>
|
||||
</layout>"
|
||||
};
|
||||
yield return new[]
|
||||
{
|
||||
|
|
@ -199,8 +215,8 @@ component-content";
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(PartialRazorViews_DoNotRenderLayoutData))]
|
||||
public async Task PartialRazorViews_DoNotRenderLayout(string actionName, string expected)
|
||||
[MemberData(nameof(RazorViewEngine_RendersPartialViewsData))]
|
||||
public async Task RazorViewEngine_RendersPartialViews(string actionName, string expected)
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
|
|
@ -289,5 +305,95 @@ View With Layout
|
|||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewComponentsExecuteLayout()
|
||||
{
|
||||
// Arrange
|
||||
var expected =
|
||||
@"<title>View With Component With Layout</title>
|
||||
|
||||
Page Content
|
||||
<component-title>ViewComponent With Title</component-title>
|
||||
<component-body>
|
||||
Component With Layout</component-body>";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/ViewEngine/ViewWithComponentThatHasLayout");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewComponentsDoNotExecuteViewStarts()
|
||||
{
|
||||
// Arrange
|
||||
var expected = @"<page-content>ViewComponent With ViewStart</page-content>";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/ViewEngine/ViewWithComponentThatHasViewStart");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PartialDoNotExecuteViewStarts()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "Partial that does not specify Layout";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/PartialsWithLayout/PartialDoesNotExecuteViewStarts");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PartialsRenderedViaRenderPartialAsync_CanRenderLayouts()
|
||||
{
|
||||
// Arrange
|
||||
var expected =
|
||||
@"<layout-for-viewstart-with-layout><layout-for-viewstart-with-layout>
|
||||
Partial that specifies Layout
|
||||
</layout-for-viewstart-with-layout>Partial that does not specify Layout
|
||||
</layout-for-viewstart-with-layout>";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/PartialsWithLayout/PartialsRenderedViaRenderPartial");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PartialsRenderedViaPartialAsync_CanRenderLayouts()
|
||||
{
|
||||
// Arrange
|
||||
var expected =
|
||||
@"<layout-for-viewstart-with-layout><layout-for-viewstart-with-layout>
|
||||
Partial that specifies Layout
|
||||
</layout-for-viewstart-with-layout>
|
||||
Partial that does not specify Layout
|
||||
</layout-for-viewstart-with-layout>";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/PartialsWithLayout/PartialsRenderedViaPartialAsync");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
#pragma warning restore 1998
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_AsPartial_DoesNotBufferOutput()
|
||||
public async Task RenderAsync_AsPartial_BuffersOutput()
|
||||
{
|
||||
// Arrange
|
||||
TextWriter actual = null;
|
||||
|
|
@ -43,7 +43,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, actual);
|
||||
Assert.NotSame(expected, actual);
|
||||
Assert.IsAssignableFrom<IBufferedTextWriter>(actual);
|
||||
Assert.Equal("Hello world", viewContext.Writer.ToString());
|
||||
}
|
||||
|
||||
|
|
@ -105,13 +106,31 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_AsPartial_DoesNotExecuteLayoutOrViewStartPages()
|
||||
public async Task RenderAsync_AsPartial_ExecutesLayout_ButNotViewStartPages()
|
||||
{
|
||||
// Arrange
|
||||
var expected = string.Join(Environment.NewLine,
|
||||
"layout-content",
|
||||
"page-content");
|
||||
var page = new TestableRazorPage(v =>
|
||||
{
|
||||
v.Layout = LayoutPath;
|
||||
v.Write("page-content");
|
||||
});
|
||||
|
||||
var layout = new TestableRazorPage(v =>
|
||||
{
|
||||
v.Write("layout-content" + Environment.NewLine);
|
||||
v.RenderBodyPublic();
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
||||
.Returns(layout);
|
||||
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
||||
.Returns(new RazorPageResult(LayoutPath, layout));
|
||||
|
||||
var viewStartProvider = CreateViewStartProvider();
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
|
|
@ -124,10 +143,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
viewEngine.Verify(v => v.FindPage(It.IsAny<ActionContext>(), It.IsAny<string>()),
|
||||
Times.Never());
|
||||
Mock.Get(viewStartProvider)
|
||||
.Verify(v => v.GetViewStartPages(It.IsAny<string>()), Times.Never());
|
||||
Assert.Equal(expected, viewContext.Writer.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace MvcSample.Web.Components
|
||||
{
|
||||
public class ComponentWithLayout : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
ViewData["Title"] = "ViewComponent With Title";
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace MvcSample.Web.Components
|
||||
{
|
||||
public class ComponentWithViewStart : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
ViewData["Title"] = "ViewComponent With ViewStart";
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace RazorWebSite.Controllers
|
||||
{
|
||||
public class PartialsWithLayoutController : Controller
|
||||
{
|
||||
public IActionResult PartialDoesNotExecuteViewStarts()
|
||||
{
|
||||
return PartialView("PartialThatDoesNotSpecifyLayout");
|
||||
}
|
||||
|
||||
// This action demonstrates
|
||||
// (a) _ViewStart does not get executed when executing a partial via RenderPartial
|
||||
// (b) Partials rendered via RenderPartial can execute Layout.
|
||||
public IActionResult PartialsRenderedViaRenderPartial()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// This action demonstrates
|
||||
// (a) _ViewStart does not get executed when executing a partial via PartialAsync
|
||||
// (b) Partials rendered via PartialAsync can execute Layout.
|
||||
public IActionResult PartialsRenderedViaPartialAsync()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -60,5 +60,16 @@ namespace RazorWebSite.Controllers
|
|||
ViewData["data-from-controller"] = "hello from controller";
|
||||
return View("ViewWithDataFromController");
|
||||
}
|
||||
|
||||
public ViewResult ViewWithComponentThatHasLayout()
|
||||
{
|
||||
ViewData["Title"] = "View With Component With Layout";
|
||||
return View();
|
||||
}
|
||||
|
||||
public ViewResult ViewWithComponentThatHasViewStart()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<layout-for-viewstart-with-layout>@RenderBody()</layout-for-viewstart-with-layout>
|
||||
|
|
@ -0,0 +1 @@
|
|||
Partial that does not specify Layout
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
Layout = "LayoutForViewStartWithLayout";
|
||||
}
|
||||
Partial that specifies Layout
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
@await Html.PartialAsync("PartialThatSpecifiesLayout")
|
||||
@await Html.PartialAsync("PartialThatDoesNotSpecifyLayout")
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
await Html.RenderPartialAsync("PartialThatSpecifiesLayout");
|
||||
await Html.RenderPartialAsync("PartialThatDoesNotSpecifyLayout");
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
Layout = "LayoutForViewStartWithLayout";
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
Layout = "_ComponentLayout";
|
||||
}
|
||||
Component With Layout
|
||||
|
|
@ -0,0 +1 @@
|
|||
@ViewBag.Title
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
throw new Exception("This should not be invoked as part of executing the View Component.");
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<component-title>@ViewBag.Title</component-title>
|
||||
<component-body>@RenderBody()</component-body>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
@{
|
||||
Layout = "_LayoutWithTitle";
|
||||
}
|
||||
Page Content
|
||||
@await Component.InvokeAsync("ComponentWithLayout")
|
||||
|
|
@ -0,0 +1 @@
|
|||
<page-content>@await Component.InvokeAsync("ComponentWithViewStart")</page-content>
|
||||
Loading…
Reference in New Issue