Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么ASP.NET视图引擎要检查.Mobile.chstml视图路径?_Asp.net_Asp.net Mvc_Asp.net Mvc 4_Razor 2 - Fatal编程技术网

为什么ASP.NET视图引擎要检查.Mobile.chstml视图路径?

为什么ASP.NET视图引擎要检查.Mobile.chstml视图路径?,asp.net,asp.net-mvc,asp.net-mvc-4,razor-2,Asp.net,Asp.net Mvc,Asp.net Mvc 4,Razor 2,对于我的ASP.NETMVC4项目,我正在尝试实现一个自定义视图引擎来查找“Index.cshtml”视图文件(如果文件夹中存在)。此外,对于所有未找到的视图路径,我将抛出一个404 404在视图文件不存在时工作。当视图文件确实存在时,视图引擎将使用FileExists()函数尝试查找.Mobile.cshtml文件。没有.mobile.cshtml文件,因此它会引发异常。为什么视图引擎在已经找到非移动文件时仍在查找.mobile.cshtml文件 例如,当视图引擎能够在“~/Views/Abo

对于我的ASP.NETMVC4项目,我正在尝试实现一个自定义视图引擎来查找“Index.cshtml”视图文件(如果文件夹中存在)。此外,对于所有未找到的视图路径,我将抛出一个404

404在视图文件不存在时工作。当视图文件确实存在时,视图引擎将使用FileExists()函数尝试查找.Mobile.cshtml文件。没有.mobile.cshtml文件,因此它会引发异常。为什么视图引擎在已经找到非移动文件时仍在查找.mobile.cshtml文件

例如,当视图引擎能够在“~/Views/About/History/Index.cshtml”找到视图路径时,它将尝试查找文件“~/Views/About/History/Index.Mobile.cshtml”。下面是我的自定义视图引擎的完整代码

namespace System.Web.Mvc
{
  // Extend where RazorViewEngine looks for view files.
  // This looks for path/index.ext file if no path.ext file is found
  // Ex:  looks for "about/history/index.chstml" if "about/history.cshtml" is not found.
  public class CustomViewEngine : RazorViewEngine
  {
    public BeckmanViewEngine()
    {
      AreaViewLocationFormats = new[] 
      {
        "~/Areas/{2}/Views/{1}/{0}/Index.cshtml",
      };

      ViewLocationFormats = new[] 
      {
        "~/Views/{1}/{0}/Index.cshtml",
      };
    }

    // Return 404 Exception if viewpath file in existing path is not found
    protected override bool FileExists(ControllerContext context, string path)
    {
      if (!base.FileExists(context, path))
      {
        throw new HttpException(404, "HTTP/1.1 404 Not Found");
      }

      return true;
    }
  }
}

我在地图上挖了一点后找到了答案

RazorViewEngine
源于
BuildManagerViewEngine
,而此引擎又源于
VirtualPathProviderViewEngine
。 它是
VirtualPathProviderViewEngine
实现方法
FindView

    public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (String.IsNullOrEmpty(viewName))
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
        }

        string[] viewLocationsSearched;
        string[] masterLocationsSearched;

        string controllerName = controllerContext.RouteData.GetRequiredString("controller");
        string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, CacheKeyPrefixView, useCache, out viewLocationsSearched);
        string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, CacheKeyPrefixMaster, useCache, out masterLocationsSearched);

        if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName)))
        {
            return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));
        }

        return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);
    }
当视图路径尚未缓存时,此处使用的
GetPath
方法将执行以下操作:

return nameRepresentsPath
           ? GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations)
           : GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, cacheKey, ref searchedLocations);
快到了!有趣的方法是
GetPathFromGeneralName
,它尝试为视图构建整个路径并检查该路径是否存在。该方法在视图引擎中注册的每个视图位置中循环,使用对当前HttpContext有效的显示模式更新视图路径,然后检查解析路径是否存在。如果是这样,则已找到视图,并将其分配给结果、缓存和返回结果路径

private string GetPathFromGeneralName(ControllerContext controllerContext, List<ViewLocation> locations, string name, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations)
{
    string result = String.Empty;
    searchedLocations = new string[locations.Count];

    for (int i = 0; i < locations.Count; i++)
    {
        ViewLocation location = locations[i];
        string virtualPath = location.Format(name, controllerName, areaName);
        DisplayInfo virtualPathDisplayInfo = DisplayModeProvider.GetDisplayInfoForVirtualPath(virtualPath, controllerContext.HttpContext, path => FileExists(controllerContext, path), controllerContext.DisplayMode);

        if (virtualPathDisplayInfo != null)
        {
            string resolvedVirtualPath = virtualPathDisplayInfo.FilePath;

            searchedLocations = _emptyLocations;
            result = resolvedVirtualPath;
            ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, virtualPathDisplayInfo.DisplayMode.DisplayModeId), result);

            if (controllerContext.DisplayMode == null)
            {
                    controllerContext.DisplayMode = virtualPathDisplayInfo.DisplayMode;
            }

            // Populate the cache for all other display modes. We want to cache both file system hits and misses so that we can distinguish
            // in future requests whether a file's status was evicted from the cache (null value) or if the file doesn't exist (empty string).
            IEnumerable<IDisplayMode> allDisplayModes = DisplayModeProvider.Modes;
            foreach (IDisplayMode displayMode in allDisplayModes)
            {
                if (displayMode.DisplayModeId != virtualPathDisplayInfo.DisplayMode.DisplayModeId)
                {
                    DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(controllerContext.HttpContext, virtualPath, virtualPathExists: path => FileExists(controllerContext, path));

                    string cacheValue = String.Empty;
                    if (displayInfoToCache != null && displayInfoToCache.FilePath != null)
                    {
                        cacheValue = displayInfoToCache.FilePath;
                    }
                    ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayMode.DisplayModeId), cacheValue);
                }
            }
            break;
        }

        searchedLocations[i] = virtualPath;
    }

    return result;
}
这(以及注释下面的一段代码:)意味着,一旦MVC 4从视图引擎中注册的视图位置找到第一条有效路径,它还将检查所有未测试的其他显示模式的视图文件是否存在,以便将信息包括在缓存中(尽管只是针对该视图位置,而不是视图引擎中的所有可用位置)。 还要注意,它是如何将lambda传递给每个测试的显示模式以检查该模式的文件是否存在的:

DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(
                      controllerContext.HttpContext, 
                      virtualPath, 
                      virtualPathExists: path => FileExists(controllerContext, path));
因此,这解释了当覆盖
FileExists
时,即使移动视图已经找到非移动视图,也会调用它

在任何情况下,显示模式都可以删除方法与添加显示模式的方法相同:在应用程序启动时更新DisplayModes集合。例如,删除移动显示模式,只保留默认和不特定的模式(您无法清除集合,或者找不到任何视图):


答案很长,但希望有意义!

您是否尝试过删除Mobile
DisplayModeProvider
。您可以通过在
应用程序\u Start
中运行以下命令来实现这一点:

var mobileDisplayMode = DisplayModeProvider.Instance.Modes.FirstOrDefault(a => a.DisplayModeId == "Mobile");

if (mobileDisplayMode != null)
{
    DisplayModeProvider.Instance.Modes.Remove(mobileDisplayMode);
}

您遇到的问题是预期的行为,因为FindView方法查询
DisplayModeProvider

是否删除了所有其他视图引擎?ViewEngines.engines.Clear();ViewEngines.engines.Add(新建MyCustomViewEngine());我首先清除了所有的视图引擎,然后在上面添加了RazorViewEngine和我的自定义视图引擎。这现在是有意义的,你的解释深度太棒了!我想这是有原因的,但不明白为什么。谢谢。
...
using System.Web.WebPages;
...

protected void Application_Start()
{
    DisplayModeProvider.Instance.Modes.Remove(
              DisplayModeProvider.Instance.Modes
                                 .Single(m => m.DisplayModeId == "Mobile"));
var mobileDisplayMode = DisplayModeProvider.Instance.Modes.FirstOrDefault(a => a.DisplayModeId == "Mobile");

if (mobileDisplayMode != null)
{
    DisplayModeProvider.Instance.Modes.Remove(mobileDisplayMode);
}