仅在 MVC 5 中的指定区域在自定义位置搜索视图文件
Search for view files in a custom location only for Specified Area in MVC 5
我正在寻找覆盖 MVC5 的 'ViewEngine' 的方式,首先,它找到我的页面.. 我已经失败了。
其次,它只在单个区域{root}/{area}/{controller}/{action}/{etc.}上运行
就我用谷歌搜索而言,我找到了几个主题和答案,但它们不符合我的需要。所以我宁愿在这里问,也许我有什么地方不对...
public class CustomAreaViewEngine:RazorViewEngine
{
public CustomAreaViewEngine()
{
var viewLocations = new[]
{
"~/App/pages/{1}/{0}.cshtml",
"~/App/pages/{1}/{0}.vbhtml"
};
AreaMasterLocationFormats = viewLocations;
AreaPartialViewLocationFormats = viewLocations;
AreaViewLocationFormats = viewLocations;
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
var viewEngineResult = base.FindPartialView(controllerContext, partialViewName, useCache);
return viewEngineResult;
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
controllerContext.RouteData.Values["controller"] = controllerContext.RouteData.Values["controller"].ToString().ToLower();
var viewEngineResult = base.FindView(controllerContext, viewName, masterName, useCache);
return viewEngineResult;
}
}
方法'FindView'returns空[第一个问题]
全局配置:
AreaRegistration.RegisterAllAreas();
ViewEngines.Engines.Add(new CustomAreaViewEngine()); // Look View Inside App/Pages/{1}/{0}.cshtml/.vbhtml
我的层次结构
Root
--/App
--/--/pages
--/--/shared (but not that shared)
--/...
--/Area
--/--/View
--/--/--/Controller
--/--/--/View(MVC BASED HIERARCHY, I Don't want to use in most case, and redirect to App)
--/--/--/...
--/--/...
--/Controller
--/View
--/--/...(MVC BASED HIERARCHY)
--/...
编辑:
编辑1:
由于@James Ellis-Jones 的回答,我所做的更改:
图片:
我的路线配置:
区域提供的路由配置:
全局配置:
我的视图引擎:
当我使用http://localhost:1422/view/home/index
时,我仍然收到一个错误,它存在于我的另一个家中(查看,与主控制器相关,而不是区域控制器。)它带来了错误的文件。
我发现的另一个问题,也没有用
我的命名空间在上次编辑中是错误的,我更改了它们,但也没有成功。
namespaces: new[] { "RavisHSB.Areas.View.Controllers" }
EDIT2:
由于@CarlosFernández 的回答,我所做的更改:
我添加 ViewEngines.Engines.Clear();
它不知何故领先了一步。但还是不行。
新Global.aspx:
我面临这个新错误:
试试这个:在 RouteConfig.cs 中你正在设置链接到你想要找到自定义视图位置的控制器的路由,添加这个:
var route = routes.MapRoute(<route params here>);
route.DataTokens["area"] = "AreaName";
这将告诉 MVC,当您沿着这条路线行驶时,您将进入区域 'AreaName'。然后它将随后寻找该区域的视图。当 MVC 在某些区域寻找它们时,您的自定义 ViewEngine 只会影响视图位置。否则它不会有任何效果,因为区域的位置格式列表是它唯一覆盖的列表。
尝试为您的引擎自定义此代码
更新 #1
private static readonly List<string> EmptyLocations;
public MyEngine()
{
ViewLocationFormats = new[]
{
"~/App/Pages/{1}/{0}.cshtml",
"~/App/Pages/{1}/{0}.vbhtml"
};
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
try
{
return System.IO.File.Exists(controllerContext.HttpContext.Server.MapPath(virtualPath));
}
catch (HttpException exception)
{
if (exception.GetHttpCode() != 0x194)
{
throw;
}
return false;
}
catch
{
return false;
}
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
var strArray = new List<string>();
var strArray2 = new List<string>();
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(viewName))
{
throw new ArgumentException("viewName must be specified.", "viewName");
}
var controllerName = controllerContext.RouteData.GetRequiredString("controller");
var viewPath = "";
var viewLocation = ViewLocationFormats;
var masterLocation = MasterLocationFormats;
var area = "";
if (controllerContext.RouteData.DataTokens.Keys.Any(x => x.ToLower() == "area"))
{
area = controllerContext.RouteData.DataTokens.FirstOrDefault(x => x.Key.ToLower() == "area").Value.ToString();
viewLocation = AreaViewLocationFormats;
masterLocation = AreaMasterLocationFormats;
}
viewPath = GetPath(controllerContext, viewLocation, area, viewName, controllerName, "TubaSite_View", useCache, strArray);
var masterPath = GetPath(controllerContext, masterLocation, area, masterName, controllerName, "TubaSite_Master", useCache, strArray2);
if (!string.IsNullOrEmpty(viewPath) && (!string.IsNullOrEmpty(masterPath) || string.IsNullOrEmpty(masterName)))
{
return new ViewEngineResult(this.CreateView(controllerContext, viewPath, masterPath), this);
}
if (string.IsNullOrEmpty(viewPath))
{
throw new Exception(String.Format("Page Not Found - {0} {1}", masterName, masterPath));
}
return new ViewEngineResult(strArray.Union<string>(strArray2));
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
var strArray = new List<string>();
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(partialViewName))
{
throw new ArgumentException("partialViewName must be specified.", "partialViewName");
}
var requiredString = controllerContext.RouteData.GetRequiredString("controller");
var partialViewLocation = PartialViewLocationFormats;
var area = "";
if (controllerContext.RouteData.DataTokens.Keys.Any(x => x.ToLower() == "area"))
{
area = controllerContext.RouteData.DataTokens.FirstOrDefault(x => x.Key.ToLower() == "area").Value.ToString();
partialViewLocation = AreaPartialViewLocationFormats.Union(PartialViewLocationFormats).ToArray();
}
var partialViewPath = "";
partialViewPath = GetPath(controllerContext, partialViewLocation, area, partialViewName, requiredString, "TubaSite_Partial", false, strArray);
return string.IsNullOrEmpty(partialViewPath) ? new ViewEngineResult(strArray) : new ViewEngineResult(CreatePartialView(controllerContext, partialViewPath), this);
}
private string GetPath(ControllerContext controllerContext, string[] locations, string area,
string name, string controllerName,
string cacheKeyPrefix, bool useCache, List<string> searchedLocations)
{
searchedLocations = EmptyLocations;
if (string.IsNullOrEmpty(name))
{
return string.Empty;
}
if ((locations == null) || (locations.Length == 0))
{
throw new InvalidOperationException("Path not found.");
}
var flag = IsSpecificPath(name);
var key = CreateCacheKey(cacheKeyPrefix, name, flag ? string.Empty : controllerName);
if (useCache)
{
var viewLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);
if (viewLocation != null)
{
return viewLocation;
}
}
if (!flag)
{
return
GetPathFromGeneralName(controllerContext, locations, area, name, controllerName, key,
searchedLocations);
}
return GetPathFromSpecificName(controllerContext, name, key, searchedLocations);
}
private static bool IsSpecificPath(string name)
{
var ch = name[0];
if (ch != '~')
{
return (ch == '/');
}
return true;
}
private string CreateCacheKey(string prefix, string name, string controllerName)
{
return string.Format(CultureInfo.InvariantCulture,
":ViewCacheEntry:{0}:{1}:{2}:{3}", GetType().AssemblyQualifiedName, prefix, name, controllerName);
}
private string GetPathFromGeneralName(ControllerContext controllerContext, IEnumerable<string> locations, string area, string name,
string controllerName, string cacheKey, List<string> searchedLocations)
{
if (locations == null) throw new ArgumentNullException("locations");
if (searchedLocations == null) searchedLocations = new List<string>();
var virtualPath = string.Empty;
var locationData =
locations.Select(
t =>
string.Format(CultureInfo.InvariantCulture, t, new object[] { name, controllerName })).ToList();
foreach (var str2 in locationData)
{
if (FileExists(controllerContext, str2))
{
virtualPath = str2;
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
searchedLocations.Add(str2);
}
return virtualPath;
}
private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, List<string> searchedLocations)
{
var virtualPath = name;
if (!FileExists(controllerContext, name))
{
virtualPath = string.Empty;
searchedLocations = new List<string>() { name };
}
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
}
对于您遇到的最后一个错误,您可以使用此配置
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.Optimization" />
</namespaces>
</pages>
</system.web.webPages.razor>
我正在寻找覆盖 MVC5 的 'ViewEngine' 的方式,首先,它找到我的页面.. 我已经失败了。
其次,它只在单个区域{root}/{area}/{controller}/{action}/{etc.}上运行
就我用谷歌搜索而言,我找到了几个主题和答案,但它们不符合我的需要。所以我宁愿在这里问,也许我有什么地方不对...
public class CustomAreaViewEngine:RazorViewEngine
{
public CustomAreaViewEngine()
{
var viewLocations = new[]
{
"~/App/pages/{1}/{0}.cshtml",
"~/App/pages/{1}/{0}.vbhtml"
};
AreaMasterLocationFormats = viewLocations;
AreaPartialViewLocationFormats = viewLocations;
AreaViewLocationFormats = viewLocations;
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
var viewEngineResult = base.FindPartialView(controllerContext, partialViewName, useCache);
return viewEngineResult;
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
controllerContext.RouteData.Values["controller"] = controllerContext.RouteData.Values["controller"].ToString().ToLower();
var viewEngineResult = base.FindView(controllerContext, viewName, masterName, useCache);
return viewEngineResult;
}
}
方法'FindView'returns空[第一个问题]
全局配置:
AreaRegistration.RegisterAllAreas();
ViewEngines.Engines.Add(new CustomAreaViewEngine()); // Look View Inside App/Pages/{1}/{0}.cshtml/.vbhtml
我的层次结构
Root
--/App
--/--/pages
--/--/shared (but not that shared)
--/...
--/Area
--/--/View
--/--/--/Controller
--/--/--/View(MVC BASED HIERARCHY, I Don't want to use in most case, and redirect to App)
--/--/--/...
--/--/...
--/Controller
--/View
--/--/...(MVC BASED HIERARCHY)
--/...
编辑:
编辑1:
由于@James Ellis-Jones 的回答,我所做的更改:
图片:
我的路线配置:
当我使用http://localhost:1422/view/home/index
时,我仍然收到一个错误,它存在于我的另一个家中(查看,与主控制器相关,而不是区域控制器。)它带来了错误的文件。
我发现的另一个问题,也没有用
我的命名空间在上次编辑中是错误的,我更改了它们,但也没有成功。
namespaces: new[] { "RavisHSB.Areas.View.Controllers" }
EDIT2:
由于@CarlosFernández 的回答,我所做的更改:
我添加 ViewEngines.Engines.Clear();
它不知何故领先了一步。但还是不行。
新Global.aspx:
试试这个:在 RouteConfig.cs 中你正在设置链接到你想要找到自定义视图位置的控制器的路由,添加这个:
var route = routes.MapRoute(<route params here>);
route.DataTokens["area"] = "AreaName";
这将告诉 MVC,当您沿着这条路线行驶时,您将进入区域 'AreaName'。然后它将随后寻找该区域的视图。当 MVC 在某些区域寻找它们时,您的自定义 ViewEngine 只会影响视图位置。否则它不会有任何效果,因为区域的位置格式列表是它唯一覆盖的列表。
尝试为您的引擎自定义此代码
更新 #1
private static readonly List<string> EmptyLocations;
public MyEngine()
{
ViewLocationFormats = new[]
{
"~/App/Pages/{1}/{0}.cshtml",
"~/App/Pages/{1}/{0}.vbhtml"
};
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
try
{
return System.IO.File.Exists(controllerContext.HttpContext.Server.MapPath(virtualPath));
}
catch (HttpException exception)
{
if (exception.GetHttpCode() != 0x194)
{
throw;
}
return false;
}
catch
{
return false;
}
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
var strArray = new List<string>();
var strArray2 = new List<string>();
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(viewName))
{
throw new ArgumentException("viewName must be specified.", "viewName");
}
var controllerName = controllerContext.RouteData.GetRequiredString("controller");
var viewPath = "";
var viewLocation = ViewLocationFormats;
var masterLocation = MasterLocationFormats;
var area = "";
if (controllerContext.RouteData.DataTokens.Keys.Any(x => x.ToLower() == "area"))
{
area = controllerContext.RouteData.DataTokens.FirstOrDefault(x => x.Key.ToLower() == "area").Value.ToString();
viewLocation = AreaViewLocationFormats;
masterLocation = AreaMasterLocationFormats;
}
viewPath = GetPath(controllerContext, viewLocation, area, viewName, controllerName, "TubaSite_View", useCache, strArray);
var masterPath = GetPath(controllerContext, masterLocation, area, masterName, controllerName, "TubaSite_Master", useCache, strArray2);
if (!string.IsNullOrEmpty(viewPath) && (!string.IsNullOrEmpty(masterPath) || string.IsNullOrEmpty(masterName)))
{
return new ViewEngineResult(this.CreateView(controllerContext, viewPath, masterPath), this);
}
if (string.IsNullOrEmpty(viewPath))
{
throw new Exception(String.Format("Page Not Found - {0} {1}", masterName, masterPath));
}
return new ViewEngineResult(strArray.Union<string>(strArray2));
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
var strArray = new List<string>();
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(partialViewName))
{
throw new ArgumentException("partialViewName must be specified.", "partialViewName");
}
var requiredString = controllerContext.RouteData.GetRequiredString("controller");
var partialViewLocation = PartialViewLocationFormats;
var area = "";
if (controllerContext.RouteData.DataTokens.Keys.Any(x => x.ToLower() == "area"))
{
area = controllerContext.RouteData.DataTokens.FirstOrDefault(x => x.Key.ToLower() == "area").Value.ToString();
partialViewLocation = AreaPartialViewLocationFormats.Union(PartialViewLocationFormats).ToArray();
}
var partialViewPath = "";
partialViewPath = GetPath(controllerContext, partialViewLocation, area, partialViewName, requiredString, "TubaSite_Partial", false, strArray);
return string.IsNullOrEmpty(partialViewPath) ? new ViewEngineResult(strArray) : new ViewEngineResult(CreatePartialView(controllerContext, partialViewPath), this);
}
private string GetPath(ControllerContext controllerContext, string[] locations, string area,
string name, string controllerName,
string cacheKeyPrefix, bool useCache, List<string> searchedLocations)
{
searchedLocations = EmptyLocations;
if (string.IsNullOrEmpty(name))
{
return string.Empty;
}
if ((locations == null) || (locations.Length == 0))
{
throw new InvalidOperationException("Path not found.");
}
var flag = IsSpecificPath(name);
var key = CreateCacheKey(cacheKeyPrefix, name, flag ? string.Empty : controllerName);
if (useCache)
{
var viewLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);
if (viewLocation != null)
{
return viewLocation;
}
}
if (!flag)
{
return
GetPathFromGeneralName(controllerContext, locations, area, name, controllerName, key,
searchedLocations);
}
return GetPathFromSpecificName(controllerContext, name, key, searchedLocations);
}
private static bool IsSpecificPath(string name)
{
var ch = name[0];
if (ch != '~')
{
return (ch == '/');
}
return true;
}
private string CreateCacheKey(string prefix, string name, string controllerName)
{
return string.Format(CultureInfo.InvariantCulture,
":ViewCacheEntry:{0}:{1}:{2}:{3}", GetType().AssemblyQualifiedName, prefix, name, controllerName);
}
private string GetPathFromGeneralName(ControllerContext controllerContext, IEnumerable<string> locations, string area, string name,
string controllerName, string cacheKey, List<string> searchedLocations)
{
if (locations == null) throw new ArgumentNullException("locations");
if (searchedLocations == null) searchedLocations = new List<string>();
var virtualPath = string.Empty;
var locationData =
locations.Select(
t =>
string.Format(CultureInfo.InvariantCulture, t, new object[] { name, controllerName })).ToList();
foreach (var str2 in locationData)
{
if (FileExists(controllerContext, str2))
{
virtualPath = str2;
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
searchedLocations.Add(str2);
}
return virtualPath;
}
private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, List<string> searchedLocations)
{
var virtualPath = name;
if (!FileExists(controllerContext, name))
{
virtualPath = string.Empty;
searchedLocations = new List<string>() { name };
}
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
}
对于您遇到的最后一个错误,您可以使用此配置
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.Optimization" />
</namespaces>
</pages>
</system.web.webPages.razor>