ViewViewComponentResult doesn't handle `~/` in view paths correctly

Fixes #2856
This commit is contained in:
Pranav K 2015-08-12 16:26:18 -07:00
parent 18c3ad25fb
commit 93d07147b2
2 changed files with 46 additions and 11 deletions

View File

@ -5,7 +5,6 @@ using System;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
@ -18,6 +17,7 @@ namespace Microsoft.AspNet.Mvc
{ {
// {0} is the component name, {1} is the view name. // {0} is the component name, {1} is the view name.
private const string ViewPathFormat = "Components/{0}/{1}"; private const string ViewPathFormat = "Components/{0}/{1}";
private const string DefaultViewName = "Default";
/// <summary> /// <summary>
/// Gets or sets the view name. /// Gets or sets the view name.
@ -63,9 +63,11 @@ namespace Microsoft.AspNet.Mvc
{ {
var viewEngine = ViewEngine ?? ResolveViewEngine(context); var viewEngine = ViewEngine ?? ResolveViewEngine(context);
var viewData = ViewData ?? context.ViewData; var viewData = ViewData ?? context.ViewData;
var isNullOrEmptyViewName = string.IsNullOrEmpty(ViewName);
string qualifiedViewName; string qualifiedViewName;
if (ViewName != null && ViewName.Length > 0 && ViewName[0] == '/') if (!isNullOrEmptyViewName &&
(ViewName[0] == '~' || ViewName[0] == '/'))
{ {
// View name that was passed in is already a rooted path, the view engine will handle this. // View name that was passed in is already a rooted path, the view engine will handle this.
qualifiedViewName = ViewName; qualifiedViewName = ViewName;
@ -83,11 +85,13 @@ namespace Microsoft.AspNet.Mvc
// Areas/Blog/Views/Shared/Components/Cart/Default.cshtml // Areas/Blog/Views/Shared/Components/Cart/Default.cshtml
// //
// This supports a controller or area providing an override for component views. // This supports a controller or area providing an override for component views.
var viewName = isNullOrEmptyViewName ? DefaultViewName : ViewName;
qualifiedViewName = string.Format( qualifiedViewName = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
ViewPathFormat, ViewPathFormat,
context.ViewComponentDescriptor.ShortName, context.ViewComponentDescriptor.ShortName,
ViewName ?? "Default"); viewName);
} }
var view = FindView(context.ViewContext, viewEngine, qualifiedViewName); var view = FindView(context.ViewContext, viewEngine, qualifiedViewName);

View File

@ -275,22 +275,53 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(expected, ex.Message); Assert.Equal(expected, ex.Message);
} }
[Fact] [Theory]
public void Execute_CallsFindPartialView_WithExpectedPath() [InlineData(null)]
[InlineData("")]
public void Execute_CallsFindPartialView_WithExpectedPath_WhenViewNameIsNullOrEmpty(string viewName)
{
// Arrange
var shortName = "SomeShortName";
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
var componentContext = GetViewComponentContext(new Mock<IView>().Object, viewData);
componentContext.ViewComponentDescriptor.ShortName = shortName;
var expectedViewName = $"Components/{shortName}/Default";
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), expectedViewName))
.Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object))
.Verifiable();
var componentResult = new ViewViewComponentResult();
componentResult.ViewEngine = viewEngine.Object;
componentResult.ViewData = viewData;
componentResult.ViewName = viewName;
// Act & Assert
componentResult.Execute(componentContext);
viewEngine.Verify();
}
[Theory]
[InlineData("~/Home/Index/MyViewComponent1.cshtml")]
[InlineData("~MyViewComponent2.cshtml")]
[InlineData("/MyViewComponent3.cshtml")]
public void Execute_CallsFindPartialView_WithExpectedPath_WhenViewNameIsSpecified(string viewName)
{ {
// Arrange // Arrange
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict); var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine viewEngine
.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), .Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), viewName))
It.Is<string>(view => view.Contains("Components"))))
.Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object)) .Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object))
.Verifiable(); .Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider()); var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
var componentContext = GetViewComponentContext(new Mock<IView>().Object, viewData); var componentContext = GetViewComponentContext(new Mock<IView>().Object, viewData);
var componentResult = new ViewViewComponentResult(); var componentResult = new ViewViewComponentResult
componentResult.ViewEngine = viewEngine.Object; {
componentResult.ViewData = viewData; ViewEngine = viewEngine.Object,
ViewData = viewData,
ViewName = viewName
};
// Act & Assert // Act & Assert
componentResult.Execute(componentContext); componentResult.Execute(componentContext);