parent
29c346ebf8
commit
a33e83f363
|
|
@ -5,8 +5,21 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the contract for a view.
|
||||
/// </summary>
|
||||
public interface IView
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the path of the view as resolved by the <see cref="IViewEngine"/>.
|
||||
/// </summary>
|
||||
string Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously renders the view using the specified <paramref name="context"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="ViewContext"/>.</param>
|
||||
/// <returns>A <see cref="Task"/> that on completion renders the view.</returns>
|
||||
Task RenderAsync([NotNull] ViewContext context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ using Microsoft.AspNet.Mvc.Rendering;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Context for view execution.
|
||||
/// </summary>
|
||||
public class ViewContext : ActionContext
|
||||
{
|
||||
// We need a default FormContext if the user uses html <form> instead of an MvcForm
|
||||
|
|
@ -14,6 +17,13 @@ namespace Microsoft.AspNet.Mvc
|
|||
private FormContext _formContext;
|
||||
private DynamicViewData _viewBag;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ViewContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="actionContext">The <see cref="ActionContext"/>.</param>
|
||||
/// <param name="view">The <see cref="IView"/> being rendered.</param>
|
||||
/// <param name="viewData">The <see cref="ViewDataDictionary"/>.</param>
|
||||
/// <param name="writer">The <see cref="TextWriter"/> to render output to.</param>
|
||||
public ViewContext(
|
||||
[NotNull] ActionContext actionContext,
|
||||
[NotNull] IView view,
|
||||
|
|
@ -31,6 +41,13 @@ namespace Microsoft.AspNet.Mvc
|
|||
ValidationMessageElement = "span";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ViewContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="viewContext">The <see cref="ViewContext"/> to copy values from.</param>
|
||||
/// <param name="view">The <see cref="IView"/> being rendered.</param>
|
||||
/// <param name="viewData">The <see cref="ViewDataDictionary"/>.</param>
|
||||
/// <param name="writer">The <see cref="TextWriter"/> to render output to.</param>
|
||||
public ViewContext(
|
||||
[NotNull] ViewContext viewContext,
|
||||
[NotNull] IView view,
|
||||
|
|
@ -49,6 +66,10 @@ namespace Microsoft.AspNet.Mvc
|
|||
Writer = writer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Mvc.FormContext"/> for the form element being rendered.
|
||||
/// A default context is returned if no form is currently being rendered.
|
||||
/// </summary>
|
||||
public virtual FormContext FormContext
|
||||
{
|
||||
get
|
||||
|
|
@ -62,6 +83,9 @@ namespace Microsoft.AspNet.Mvc
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether client-side validation is enabled.
|
||||
/// </summary>
|
||||
public bool ClientValidationEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -84,6 +108,9 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// </summary>
|
||||
public string ValidationMessageElement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dynamic view bag.
|
||||
/// </summary>
|
||||
public dynamic ViewBag
|
||||
{
|
||||
get
|
||||
|
|
@ -97,12 +124,30 @@ namespace Microsoft.AspNet.Mvc
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IView"/> currently being rendered, if any.
|
||||
/// </summary>
|
||||
public IView View { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ViewDataDictionary"/>.
|
||||
/// </summary>
|
||||
public ViewDataDictionary ViewData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TextWriter"/> used to write the output.
|
||||
/// </summary>
|
||||
public TextWriter Writer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path of the view file currently being rendered.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The rendering of a view may involve one or more files (e.g. _ViewStart, Layouts etc).
|
||||
/// This property contains the path of the file currently being rendered.
|
||||
/// </remarks>
|
||||
public string ExecutingFilePath { get; set; }
|
||||
|
||||
public FormContext GetFormContextForClientValidation()
|
||||
{
|
||||
return (ClientValidationEnabled) ? FormContext : null;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
IsPartial = isPartial;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Path
|
||||
{
|
||||
get { return RazorPage.Path; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="IRazorPage"/> instance that the views executes on.
|
||||
/// </summary>
|
||||
|
|
@ -93,7 +99,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
// The writer for the body is passed through the ViewContext, allowing things like HtmlHelpers
|
||||
// and ViewComponents to reference it.
|
||||
var oldWriter = context.Writer;
|
||||
var oldFilePath = context.ExecutingFilePath;
|
||||
context.Writer = writer;
|
||||
context.ExecutingFilePath = page.Path;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -109,6 +117,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
finally
|
||||
{
|
||||
context.Writer = oldWriter;
|
||||
context.ExecutingFilePath = oldFilePath;
|
||||
writer.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -131,12 +140,21 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var viewStarts = _viewStartProvider.GetViewStartPages(RazorPage.Path);
|
||||
|
||||
string layout = null;
|
||||
foreach (var viewStart in viewStarts)
|
||||
var oldFilePath = context.ExecutingFilePath;
|
||||
try
|
||||
{
|
||||
// Copy the layout value from the previous view start (if any) to the current.
|
||||
viewStart.Layout = layout;
|
||||
await RenderPageCoreAsync(viewStart, context);
|
||||
layout = viewStart.Layout;
|
||||
foreach (var viewStart in viewStarts)
|
||||
{
|
||||
context.ExecutingFilePath = viewStart.Path;
|
||||
// Copy the layout value from the previous view start (if any) to the current.
|
||||
viewStart.Layout = layout;
|
||||
await RenderPageCoreAsync(viewStart, context);
|
||||
layout = viewStart.Layout;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.ExecutingFilePath = oldFilePath;
|
||||
}
|
||||
|
||||
// Copy over interesting properties from the ViewStart page to the entry page.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<Layout>
|
||||
/Views/ViewWithPaths/_Layout.cshtml
|
||||
/Views/ViewWithPaths/Index.cshtml
|
||||
</Layout>
|
||||
|
||||
<ViewStart>
|
||||
Views\ViewWithPaths\_ViewStart.cshtml
|
||||
/Views/ViewWithPaths/Index.cshtml
|
||||
</ViewStart>
|
||||
<Index>
|
||||
/Views/ViewWithPaths/Index.cshtml
|
||||
/Views/ViewWithPaths/Index.cshtml
|
||||
<component>
|
||||
/Views/Shared/Components/ComponentForViewWithPaths/Default.cshtml
|
||||
/Views/Shared/Components/ComponentForViewWithPaths/Default.cshtml
|
||||
</component>
|
||||
<Partial>
|
||||
/Views/ViewWithPaths/_Partial.cshtml
|
||||
/Views/ViewWithPaths/_Partial.cshtml
|
||||
</Partial>
|
||||
</Index>
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
|
|
@ -395,5 +396,21 @@ Partial that does not specify Layout
|
|||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RazorView_SetsViewPathAndExecutingPagePath()
|
||||
{
|
||||
// Arrange
|
||||
var expected = await GetType().GetTypeInfo().Assembly
|
||||
.ReadResourceAsStringAsync("compiler/resources/ViewEngineController.ViewWithPaths.txt");
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/ViewWithPaths");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -83,6 +84,61 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
Assert.Same(expectedWriter, viewContext.Writer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewContext_ExecutingPagePath_ReturnsPathOfRazorPageBeingExecuted()
|
||||
{
|
||||
// Arrange
|
||||
var pagePath = "/my/view";
|
||||
var paths = new List<string>();
|
||||
var page = new TestableRazorPage(v =>
|
||||
{
|
||||
paths.Add(v.ViewContext.ExecutingFilePath);
|
||||
Assert.Equal(pagePath, v.ViewContext.View.Path);
|
||||
})
|
||||
{
|
||||
Path = pagePath
|
||||
};
|
||||
|
||||
var viewStart = new TestableRazorPage(v =>
|
||||
{
|
||||
v.Layout = LayoutPath;
|
||||
paths.Add(v.ViewContext.ExecutingFilePath);
|
||||
Assert.Equal(pagePath, v.ViewContext.View.Path);
|
||||
})
|
||||
{
|
||||
Path = "_ViewStart"
|
||||
};
|
||||
|
||||
var layout = new TestableRazorPage(v =>
|
||||
{
|
||||
v.RenderBodyPublic();
|
||||
paths.Add(v.ViewContext.ExecutingFilePath);
|
||||
Assert.Equal(pagePath, v.ViewContext.View.Path);
|
||||
})
|
||||
{
|
||||
Path = LayoutPath
|
||||
};
|
||||
|
||||
var activator = Mock.Of<IRazorPageActivator>();
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
||||
.Returns(new RazorPageResult(LayoutPath, layout));
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
activator,
|
||||
CreateViewStartProvider(viewStart),
|
||||
page,
|
||||
isPartial: false);
|
||||
|
||||
var viewContext = CreateViewContext(view);
|
||||
var expectedWriter = viewContext.Writer;
|
||||
|
||||
// Act
|
||||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { "_ViewStart", pagePath, LayoutPath }, paths);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_AsPartial_ActivatesViews()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ namespace CompositeViewEngineWebSite
|
|||
{
|
||||
public class TestPartialView : IView
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public async Task RenderAsync(ViewContext context)
|
||||
{
|
||||
await context.Writer.WriteLineAsync("world");
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ namespace CompositeViewEngineWebSite
|
|||
{
|
||||
public class TestView : IView
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public async Task RenderAsync(ViewContext context)
|
||||
{
|
||||
await context.Writer.WriteLineAsync("Content from test view");
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ namespace ModelBindingWebSite.Controllers
|
|||
|
||||
private sealed class TestView : IView
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public Task RenderAsync(ViewContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
[ViewComponent(Name = "ComponentForViewWithPaths")]
|
||||
public class ComponentForViewWithPaths : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
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 RazorWebSite.Controllers
|
||||
{
|
||||
public class ViewWithPathsController : Controller
|
||||
{
|
||||
[HttpGet("/ViewWithPaths")]
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<component>
|
||||
@ViewContext.ExecutingFilePath
|
||||
@ViewContext.View.Path
|
||||
</component>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<Index>
|
||||
@ViewContext.ExecutingFilePath
|
||||
@ViewContext.View.Path
|
||||
@Component.Invoke("ComponentForViewWithPaths")
|
||||
@Html.Partial("_Partial")
|
||||
</Index>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<Layout>
|
||||
@ViewContext.ExecutingFilePath
|
||||
@ViewContext.View.Path
|
||||
</Layout>
|
||||
@RenderBody()
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<Partial>
|
||||
@ViewContext.ExecutingFilePath
|
||||
@ViewContext.View.Path
|
||||
</Partial>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
<ViewStart>
|
||||
@ViewContext.ExecutingFilePath
|
||||
@ViewContext.View.Path
|
||||
</ViewStart>
|
||||
Loading…
Reference in New Issue