From d46389888d4ec6f388999073b71ecaf342b2652b Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Tue, 15 Apr 2014 14:46:49 -0700 Subject: [PATCH] Modify search location for views. When areas are not provided area locations are not searched for views. Also added tests for search locations in RazorViewEngine failures. --- .../ViewEngine/RazorViewEngine.cs | 43 ++++++-- .../Microsoft.AspNet.Mvc.Razor.Test.kproj | 1 + .../RazorViewEngineTest.cs | 103 ++++++++++++++++++ 3 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs diff --git a/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/RazorViewEngine.cs b/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/RazorViewEngine.cs index 4aa4d0c943..f894c6f841 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/RazorViewEngine.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/ViewEngine/RazorViewEngine.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Threading.Tasks; +using System.Linq; using Microsoft.AspNet.Mvc.Rendering; namespace Microsoft.AspNet.Mvc.Razor @@ -9,10 +9,15 @@ namespace Microsoft.AspNet.Mvc.Razor public class RazorViewEngine : IViewEngine { private static readonly string[] _viewLocationFormats = + { + "/Views/{1}/{0}.cshtml", + "/Views/Shared/{0}.cshtml", + }; + + private static readonly string[] _areaViewLocationFormats = { "/Areas/{2}/Views/{1}/{0}.cshtml", "/Areas/{2}/Views/Shared/{0}.cshtml", - "/Views/{1}/{0}.cshtml", "/Views/Shared/{0}.cshtml", }; @@ -56,27 +61,47 @@ namespace Microsoft.AspNet.Mvc.Razor { var controllerName = context.GetValueOrDefault("controller"); var areaName = context.GetValueOrDefault("area"); + var potentialPaths = GetViewSearchPaths(viewName, controllerName, areaName); - var searchedLocations = new List(_viewLocationFormats.Length); - for (int i = 0; i < _viewLocationFormats.Length; i++) + foreach (var path in potentialPaths) { - var path = String.Format(CultureInfo.InvariantCulture, _viewLocationFormats[i], viewName, controllerName, areaName); - IView view = _virtualPathFactory.CreateInstance(path); + var view = _virtualPathFactory.CreateInstance(path); if (view != null) { return ViewEngineResult.Found(viewName, view); } - searchedLocations.Add(path); } - return ViewEngineResult.NotFound(viewName, searchedLocations); + return ViewEngineResult.NotFound(viewName, potentialPaths); } } private static bool IsSpecificPath(string name) { char c = name[0]; - return (name[0] == '/'); + return name[0] == '~' || name[0] == '/'; + } + + private IEnumerable GetViewSearchPaths(string viewName, string controllerName, string areaName) + { + IEnumerable unformattedPaths; + + if (string.IsNullOrEmpty(areaName)) + { + // If no areas then no need to search area locations. + unformattedPaths = _viewLocationFormats; + } + else + { + // If there's an area provided only search area view locations + unformattedPaths = _areaViewLocationFormats; + } + + var formattedPaths = unformattedPaths.Select(path => + string.Format(CultureInfo.InvariantCulture, path, viewName, controllerName, areaName) + ); + + return formattedPaths; } } } diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Microsoft.AspNet.Mvc.Razor.Test.kproj b/test/Microsoft.AspNet.Mvc.Razor.Test/Microsoft.AspNet.Mvc.Razor.Test.kproj index 45daf3311b..457611a8e0 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/Microsoft.AspNet.Mvc.Razor.Test.kproj +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Microsoft.AspNet.Mvc.Razor.Test.kproj @@ -23,6 +23,7 @@ + diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs new file mode 100644 index 0000000000..84a4543557 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineTest.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Mvc.Rendering; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Razor.Test +{ + public class RazorViewEngineTest + { + private static readonly Dictionary _areaTestContext = new Dictionary() + { + {"area", "foo"}, + {"controller", "bar"}, + }; + + private static readonly Dictionary _controllerTestContext = new Dictionary() + { + {"controller", "bar"}, + }; + + [Fact] + public void FindPartialViewFailureSearchesCorrectLocationsWithAreas() + { + // Arrange + var searchedLocations = new List(); + var viewEngine = CreateSearchLocationViewEngineTester(); + + // Act + var result = viewEngine.FindPartialView(_areaTestContext, "partial"); + + // Assert + Assert.False(result.Success); + Assert.Equal(new[] { + "/Areas/foo/Views/bar/partial.cshtml", + "/Areas/foo/Views/Shared/partial.cshtml", + "/Views/Shared/partial.cshtml", + }, result.SearchedLocations); + } + + [Fact] + public void FindPartialViewFailureSearchesCorrectLocationsWithoutAreas() + { + // Arrange + var viewEngine = CreateSearchLocationViewEngineTester(); + + // Act + var result = viewEngine.FindPartialView(_controllerTestContext, "partialNoArea"); + + // Assert + Assert.False(result.Success); + Assert.Equal(new[] { + "/Views/bar/partialNoArea.cshtml", + "/Views/Shared/partialNoArea.cshtml", + }, result.SearchedLocations); + } + + [Fact] + public void FindViewFailureSearchesCorrectLocationsWithAreas() + { + // Arrange + var viewEngine = CreateSearchLocationViewEngineTester(); + + // Act + var result = viewEngine.FindView(_areaTestContext, "full"); + + // Assert + Assert.False(result.Success); + Assert.Equal(new[] { + "/Areas/foo/Views/bar/full.cshtml", + "/Areas/foo/Views/Shared/full.cshtml", + "/Views/Shared/full.cshtml", + }, result.SearchedLocations); + } + + [Fact] + public void FindViewFailureSearchesCorrectLocationsWithoutAreas() + { + // Arrange + var viewEngine = CreateSearchLocationViewEngineTester(); + + // Act + var result = viewEngine.FindView(_controllerTestContext, "fullNoArea"); + + // Assert + Assert.False(result.Success); + Assert.Equal(new[] { + "/Views/bar/fullNoArea.cshtml", + "/Views/Shared/fullNoArea.cshtml", + }, result.SearchedLocations); + } + + private IViewEngine CreateSearchLocationViewEngineTester() + { + var virtualPathFactory = new Mock(); + virtualPathFactory.Setup(vpf => vpf.CreateInstance(It.IsAny())).Returns(null); + + var viewEngine = new RazorViewEngine(virtualPathFactory.Object); + + return viewEngine; + } + } +} \ No newline at end of file