【问题标题】:Search for view files in a custom location only for Specified Area in MVC 5仅在 MVC 5 中的指定区域搜索自定义位置的视图文件
【发布时间】:2016-08-11 12:36:16
【问题描述】:

我希望以首先找到我的页面的方式覆盖 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'返回空[第一个问题]

全局配置:

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)
--/...

编辑:


EDIT1:


由于@James Ellis-Jones 的回答,我所做的更改:

图片: 我的路线配置: 区域提供的路线配置: 全局配置: 我的视图引擎:

当我仍然使用http://localhost:1422/view/home/index 时,我收到一个错误,该错误存在于我的另一个家中(视图,与主控制器相关,而不是区域控制器。)它带来了错误的文件。

我发现的另一个问题,也没有用

我的命名空间在上次编辑时出错了,我更改了它们,但也没有成功。

namespaces: new[] { "RavisHSB.Areas.View.Controllers" }

EDIT2:


由于@CarlosFernández 的回答,我所做的更改:

我添加了ViewEngines.Engines.Clear(); 它不知何故领先了一步。但还是不行。

新的 Global.aspx: 我面临这个新错误:

【问题讨论】:

  • 嗨,关于您的自定义区域视图引擎的一件事是,它只更改区域请求的视图路径:因此,据我所知,控制器寻找视图必须通过路由到达在 AreaRegistrationContext 中注册,或者您必须手动将 DataToken 添加到带有索引“区域”和区域名称(在本例中为任何一个)作为值的 RouteData
  • @JamesEllis-Jones 我不知道它到底是如何工作的,我也不知道 dataToken,顺便说一句,msdn 也没有说太多...msdn.microsoft.com/en-us/library/…。忽略我的代码,我应该如何实现它。我希望地址为/View (it's my Area name, for my angular based app)/controller(same to my page folder)/Action 的所有内容都被解析为:~/App/pages/{same folder as controller name(start with small cap)}/{action name(small cap again)}.cshtml
  • 在添加您的 CustomAreaViewEngine 之前,您是否尝试过清除 viewEngines?只需在添加之前输入:ViewEngines.Engines.Clear();
  • @CarlosFernández 不,因为我希望我的主控制器也搜索主应用程序,但是在他对自己帖子的最后评论中考虑到@@JamesEllis-Jones,我想我试一试
  • @deadManN 你有多少个区域?

标签: asp.net-mvc asp.net-mvc-5 routing viewengine


【解决方案1】:

尝试为您的引擎自定义此代码

更新 #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>

【讨论】:

  • 太复杂了...顺便说一句,仅供参考,如果您可以简化它,我只有标准参数,如区域、控制器和动作。并且仅适用于这三个:AreaViewLocationFormats、AreaMasterLocationFormats、AreaPartialViewLocationFormats,我想将~/View/{controller}/{action} 解析为~/App/pages/{controller(FirstLetterSmallCap)}/{action(FirstLetterSmallCap)},View 是一个特定的区域名称,我的意思是`当 {area} == "View" 然后使用这个路径,否则不要不。顺便说一句,今天是赏金的最后一天
  • 让我补充一下,我已经找到了一种将我的页面重定向到页面的方法,正如您在页面末尾的错误中看到的那样,但我有点收到错误说我的视图必须驱动来自 WebViewPage。在 area/view/views/web.config 文件 &lt;pages pageBaseType="System.Web.Mvc.WebViewPage"&gt; 中配置
  • 请检查我的更新,我已经简化了引擎,并发布了最后一个错误的配置。希望对你有用
  • 顺便说一句,我在 Main web.config 中设置了配置
  • 既然你提供了完整的答案,我认为你应该得到这个奖项。顺便说一句,我通过将 web.config 文件从 Views 文件夹移动到 App 文件夹使其工作,因为它们都充当路径的 {controller} 部分,或者至少我是这么认为的。
【解决方案2】:

试试这个:在 RouteConfig.cs 中,您要设置链接到要查找自定义视图位置的控制器的路由,添加:

var route = routes.MapRoute(<route params here>);
route.DataTokens["area"] = "AreaName";

这将告诉 MVC,当您沿着这条路线走时,您将进入区域“AreaName”。然后它将随后在该区域寻找视图。当 MVC 在某个区域寻找视图位置时,您的自定义 ViewEngine 只会影响视图位置。否则它不会有任何效果,因为区域的位置格式列表是它唯一覆盖的。

【讨论】:

  • 我不想只注册一个区域,我想告诉它在特定位置查找文件。否则,将提供这些:AreaRegistration.RegisterAllAreas();,然后每个区域都附带:public class ViewAreaRegistration : AreaRegistration { public override string AreaName {get{return "View";}} public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "View_default", "View/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional }, namespaces: new []{"RavisHSB.Areas.View.Controllers"} );}}
  • 顺便说一句,我不想​​将 URL 路由到 MVC 控制器,而是在调用控制器操作后路由到目录。就像你说的,public ActionResult Index() { return View(); },这个返回视图从我定义的模式中带来文件,而不是来自~/{Area}/{Controller}/{Action}.cshtml
  • 我知道你不想注册一个区域,但是你需要按照我说的让你的视图引擎做任何不同于标准视图引擎的事情,因为目前它只会改变视图MVC 在某个区域中查找视图时的搜索路径
  • 这个按区域定义的呢? public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "View_default", "View/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional }, namespaces: new []{"RavisHSB.Areas.View.Controllers"} ); }
  • 过一会儿看看我的帖子,我会写完我所做的,但它仍然失败。
猜你喜欢
  • 2010-10-12
  • 1970-01-01
  • 2021-06-21
  • 2017-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多