4

有人可以解释路由如何与 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() 会正确处理该请求。

但是,对于我的生活,我无法弄清楚 url /Home/Index 是如何指向 HomeController 的。据我所知,视图 aspx 没有引用 HomeController,HomeController 没有引用视图,路由表也没有明确提到 HomeController。这是如何神奇地发生的?当然,我错过了一些明显的东西。

然后

4

3 回答 3

7

这是 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 的管道,我会推荐你​​这篇优秀的文章

于 2009-10-20T17:11:17.420 回答
5

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

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

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

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

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为您添加控制器名称——以及默认操作。

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

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

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

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

来自ASP.NET MVC 书呆子晚餐示例章节

默认情况下,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 等传递给该操作。

于 2009-10-20T17:32:32.953 回答
2

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

于 2009-10-20T17:07:53.353 回答