【问题标题】:ASP.NET MVC 2 How are urls/routes, views related to controllers?ASP.NET MVC 2 urls/routes,视图如何与控制器相关?
【发布时间】:2010-12-08 10:24:20
【问题描述】:

有人能解释一下 MVC 2 中的路由是如何与控制器相关联的吗?目前,我在 /Controllers/HomeController.cs 中有一个控制器和一个视图 /Home/Index.aspx。

我的路由注册方法是这样的:

 public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
            routes.MapRoute(
               "Default",
                // Route name
               "{controller}/{action}/{id}",
                // URL with parameters
               new { controller = "Home", action = "Index", id = "" }
                // Parameter defaults
              );
        }

如果我请求 URL:http://localhost/Home/Index,那么 HomeController.Index() 会正确处理该请求。

但是,在我的一生中,我无法弄清楚 /Home/Index url 是如何指向 HomeController 的。据我所知,视图 aspx 没有引用 HomeController,HomeController 没有引用视图,路由表也没有明确提到 HomeController。这是如何神奇地发生的?我肯定遗漏了一些明显的东西。

然后

【问题讨论】:

    标签: asp.net asp.net-mvc-2 asp.net-mvc-routing


    【解决方案1】:

    这是 ASP.NET MVC 中的约定。

    当使用 DefaultControllerFactory 时,这个约定被隐藏在内部密封类 System.Web.Mvc.ControllerTypeCache 中(典型的 Microsoft 编写内部密封类)。在里面你会发现一个叫做EnsureInitialized的方法,看起来像这样:

    public void EnsureInitialized(IBuildManager buildManager)
    {
        if (this._cache == null)
        {
            lock (this._lockObj)
            {
                if (this._cache == null)
                {
                    this._cache = GetAllControllerTypes(buildManager).GroupBy<Type, string>(delegate (Type t) {
                        return t.Name.Substring(0, t.Name.Length - "Controller".Length);
                    }, StringComparer.OrdinalIgnoreCase).ToDictionary<IGrouping<string, Type>, string, ILookup<string, Type>>(delegate (IGrouping<string, Type> g) {
                        return g.Key;
                    }, delegate (IGrouping<string, Type> g) {
                        return g.ToLookup<Type, string>(t => t.Namespace ?? string.Empty, StringComparer.OrdinalIgnoreCase);
                    }, StringComparer.OrdinalIgnoreCase);
                }
            }
        }
    }
    

    注意分组的方式。所以基本上 DefaultControllerFactory 将在所有引用的程序集中查找实现 Controller 基类的类型,并将从名称中删除“Controller”。

    如果你真的想详细剖析 ASP.NET MVC 的管道,我会推荐你​​这个excellent article

    【讨论】:

    • 这很奇怪。控制器类的名称必须和URL相关?!如果我想对没有控制器名称的 url 使用相同的控制器怎么办?
    • 然后你在你的路由表中指定它:routes.MapRoute("MyRoute", "someSpecialUrl/blabla", new { controller = "Home", action = "Index", id = "" }); 但你仍然需要指定要使用的控制器。
    • 另外,设置了 StringComparer.OrdinalIgnoreCase 标志是不是意味着框架将无法区分 HomeController 和 homecontroller?
    • @ztech:是的,但 Windows 文件系统也是如此 - 您不能(在基于 Windows 的服务器上)拥有一个名为 HomeController.cs 的文件和一个名为 homecontroller.cs 的文件具有不同的内容。诚然,您可以在一个名为 that 的应用程序中有两个类,但标准做法通常是每个文件有一个类,并且文件名与类名相同;)
    • +1 嗯,ztech 的答案很棒,cmets 也很棒,但是 4 次投票而不是选定的答案?我想你被骗了。
    【解决方案2】:

    ASP.NET MVC 附带的默认视图引擎遵循以下约定:

    你有一个这样的文件夹结构:

    - Controllers\
    |-  HomeController.cs
    - Views\
    |- Home\
    |-- Index.aspx
    |- Shared\
    

    当一个请求进来,并且匹配一个在 RegisterRoutes 方法中定义的路由时(更多信息参见URL routing),然后匹配的控制器被调用:

    routes.MapRoute(
      "Default", // Route name, allows you to call this route elsewhere
      "{controller}/{action}/{id}", // URL with parameters
      new { controller = "Home", action = "Index", id = "" } // Parameter defaults
      );
    

    在默认路由中,您还指定了默认控制器(不带“Controller”后缀)- 路由引擎会自动为您添加 Controller 到控制器名称中 - 以及默认操作。

    在您的控制器中,您调用简单方法:

    public ActionResult Index(){
      return View();
    }
    

    然后,默认视图引擎会在“Views”文件夹(约定)中名为“Home”(与控制器相同)的文件夹中查找名为 Index(与操作相同)的 aspx 文件。

    如果在其中找不到,它还会在共享文件夹中查找索引页。

    来自ASP.NET MVC Nerd Dinner sample chapter

    默认情况下,ASP.NET MVC 应用程序在解析视图模板时使用基于约定的目录命名结构。这允许开发人员在从 Controller 类中引用视图时避免必须完全限定位置路径。默认情况下,ASP.NET MVC 将在应用程序下方的\Views\[ControllerName]\ 目录中查找视图模板文件。

    \Views\Shared 子目录提供了一种存储视图模板的方法,这些模板可以在应用程序内的多个控制器中重复使用。当 ASP.NET MVC 尝试解析视图模板时,它会首先在 \Views\[Controller] 特定目录中查找,如果在其中找不到视图模板,它将在 \Views\Shared 目录中查找。

    在命名单个视图模板时,建议的指导是让视图模板与导致它呈现的操作方法共享相同的名称。例如,上面我们的“Index”操作方法是使用“Index”视图来渲染视图结果,“Details”操作方法是使用“Details”视图来渲染其结果。这样可以轻松快速地查看与每个操作关联的模板。

    当视图模板与控制器上调用的操作方法同名时,开发人员不需要显式指定视图模板名称。相反,我们可以将模型对象传递给 View() 辅助方法(无需指定视图名称),ASP.NET MVC 将自动推断我们要使用磁盘上的 \Views\[ControllerName]\[ActionName] 视图模板来呈现它。


    编辑添加:

    我设置的一些明确设置控制器的示例路由是:

    routes.MapRoute(
      "PhotoDetailsSlug",
      "Albums/{albumId}/{photoId}/{slug}",
      new {controller = "Albums", action = "PhotoDetails"},
      new {albumId = @"\d{1,4}", photoId = @"\d{1,8}"}
      );
    

    在这里,我明确声明我正在使用相册控制器和 PhotoDetails 操作,并将各种 id 等传递给该操作。

    【讨论】:

    • 大部分都是有道理的。但是,在您的第二个代码 sn-p 中传递给 .MapRoute 的默认对象引用了一个名为“Home”的控制器。我的控制器类称为“HomeController”。如何明确指定用于特定路由/url 的控制器?
    • 您需要为这些路由添加一个显式规则,并在默认参数中告诉它要使用哪个控制器(减去“控制器”后缀)。
    • 谢谢 - 我会将此标记为已接受。但是,我是否理解正确,在上面的示例中,“Albums”必须有一个名为“AlbumsController”的相应控制器类?
    • 是的,因为我已将控制器设置为“相册”,如果我将其设置为“照片”,则需要一个名为“照片控制器”的控制器。
    【解决方案3】:

    在操作索引中有一个声明return View()。当返回一个空白视图时,DefaultViewEngine 会在几个默认文件夹中搜索控制器方法的名称(特别是在 FindView 方法中)。其中之一是 Views/Home 目录,因为 Home 是控制器的名称。它在那里找到索引文件,并使用它来显示结果。

    【讨论】:

    • 这说明了如何找到正确的View,但是如何找到正确的控制器执行方法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多