parent
29c346ebf8
commit
a33e83f363
|
|
@ -5,8 +5,21 @@ using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Rendering
|
namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the contract for a view.
|
||||||
|
/// </summary>
|
||||||
public interface IView
|
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);
|
Task RenderAsync([NotNull] ViewContext context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Context for view execution.
|
||||||
|
/// </summary>
|
||||||
public class ViewContext : ActionContext
|
public class ViewContext : ActionContext
|
||||||
{
|
{
|
||||||
// We need a default FormContext if the user uses html <form> instead of an MvcForm
|
// 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 FormContext _formContext;
|
||||||
private DynamicViewData _viewBag;
|
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(
|
public ViewContext(
|
||||||
[NotNull] ActionContext actionContext,
|
[NotNull] ActionContext actionContext,
|
||||||
[NotNull] IView view,
|
[NotNull] IView view,
|
||||||
|
|
@ -31,6 +41,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
ValidationMessageElement = "span";
|
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(
|
public ViewContext(
|
||||||
[NotNull] ViewContext viewContext,
|
[NotNull] ViewContext viewContext,
|
||||||
[NotNull] IView view,
|
[NotNull] IView view,
|
||||||
|
|
@ -49,6 +66,10 @@ namespace Microsoft.AspNet.Mvc
|
||||||
Writer = writer;
|
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
|
public virtual FormContext FormContext
|
||||||
{
|
{
|
||||||
get
|
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; }
|
public bool ClientValidationEnabled { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -84,6 +108,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ValidationMessageElement { get; set; }
|
public string ValidationMessageElement { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the dynamic view bag.
|
||||||
|
/// </summary>
|
||||||
public dynamic ViewBag
|
public dynamic ViewBag
|
||||||
{
|
{
|
||||||
get
|
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; }
|
public IView View { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ViewDataDictionary"/>.
|
||||||
|
/// </summary>
|
||||||
public ViewDataDictionary ViewData { get; set; }
|
public ViewDataDictionary ViewData { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="TextWriter"/> used to write the output.
|
||||||
|
/// </summary>
|
||||||
public TextWriter Writer { get; set; }
|
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()
|
public FormContext GetFormContextForClientValidation()
|
||||||
{
|
{
|
||||||
return (ClientValidationEnabled) ? FormContext : null;
|
return (ClientValidationEnabled) ? FormContext : null;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
IsPartial = isPartial;
|
IsPartial = isPartial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get { return RazorPage.Path; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets <see cref="IRazorPage"/> instance that the views executes on.
|
/// Gets <see cref="IRazorPage"/> instance that the views executes on.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -93,7 +99,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
// The writer for the body is passed through the ViewContext, allowing things like HtmlHelpers
|
// The writer for the body is passed through the ViewContext, allowing things like HtmlHelpers
|
||||||
// and ViewComponents to reference it.
|
// and ViewComponents to reference it.
|
||||||
var oldWriter = context.Writer;
|
var oldWriter = context.Writer;
|
||||||
|
var oldFilePath = context.ExecutingFilePath;
|
||||||
context.Writer = writer;
|
context.Writer = writer;
|
||||||
|
context.ExecutingFilePath = page.Path;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -109,6 +117,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
context.Writer = oldWriter;
|
context.Writer = oldWriter;
|
||||||
|
context.ExecutingFilePath = oldFilePath;
|
||||||
writer.Dispose();
|
writer.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,12 +140,21 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
var viewStarts = _viewStartProvider.GetViewStartPages(RazorPage.Path);
|
var viewStarts = _viewStartProvider.GetViewStartPages(RazorPage.Path);
|
||||||
|
|
||||||
string layout = null;
|
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.
|
foreach (var viewStart in viewStarts)
|
||||||
viewStart.Layout = layout;
|
{
|
||||||
await RenderPageCoreAsync(viewStart, context);
|
context.ExecutingFilePath = viewStart.Path;
|
||||||
layout = viewStart.Layout;
|
// 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.
|
// 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.TestHost;
|
using Microsoft.AspNet.TestHost;
|
||||||
|
|
@ -395,5 +396,21 @@ Partial that does not specify Layout
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expected, body.Trim());
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -83,6 +84,61 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
Assert.Same(expectedWriter, viewContext.Writer);
|
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]
|
[Fact]
|
||||||
public async Task RenderAsync_AsPartial_ActivatesViews()
|
public async Task RenderAsync_AsPartial_ActivatesViews()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ namespace CompositeViewEngineWebSite
|
||||||
{
|
{
|
||||||
public class TestPartialView : IView
|
public class TestPartialView : IView
|
||||||
{
|
{
|
||||||
|
public string Path { get; set; }
|
||||||
|
|
||||||
public async Task RenderAsync(ViewContext context)
|
public async Task RenderAsync(ViewContext context)
|
||||||
{
|
{
|
||||||
await context.Writer.WriteLineAsync("world");
|
await context.Writer.WriteLineAsync("world");
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ namespace CompositeViewEngineWebSite
|
||||||
{
|
{
|
||||||
public class TestView : IView
|
public class TestView : IView
|
||||||
{
|
{
|
||||||
|
public string Path { get; set; }
|
||||||
|
|
||||||
public async Task RenderAsync(ViewContext context)
|
public async Task RenderAsync(ViewContext context)
|
||||||
{
|
{
|
||||||
await context.Writer.WriteLineAsync("Content from test view");
|
await context.Writer.WriteLineAsync("Content from test view");
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,8 @@ namespace ModelBindingWebSite.Controllers
|
||||||
|
|
||||||
private sealed class TestView : IView
|
private sealed class TestView : IView
|
||||||
{
|
{
|
||||||
|
public string Path { get; set; }
|
||||||
|
|
||||||
public Task RenderAsync(ViewContext context)
|
public Task RenderAsync(ViewContext context)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
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