6

我有一个基本的 MVC 2 (RC2) 站点,带有一个基本控制器(“Home”),一个区域(“Admin”)和一个控制器(“Abstract”)。当我调用http://website/Abstract - 管理区域中的抽象控制器被调用,即使我没有在 URL 中指定区域。更糟糕的是 - 它似乎不知道它在 Admin 下,因为它找不到关联的视图并只是返回:

The view 'Index' or its master was not found. The following locations were searched:
~/Views/Abstract/Index.aspx
~/Views/Abstract/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx

难道我做错了什么?这是一个错误吗?一个特征?

4

2 回答 2

12

我和我的朋友在使用 ASP.NET MVC 2 中的区域时遇到了同样的问题。我们发现了一个“hack”,到目前为止,它似乎正在工作。对于 tl;dr 版本,请参阅此答案的底部。

您的“Admin”区域的“AdminAreaRegistration.cs”类中可能有类似于以下内容的内容:

// Web/Areas/Admin/AdminAreaRegistration.cs

public override void RegisterArea(AreaRegistrationContext context) {
    context.MapRoute(
        "Admin_default",
        "Admin/{controller}/{action}/{id}",
        new { action = "Index", id = UrlParameter.Optional }
    );
}

因此,当您向“ http://website/Abstract ”发出请求时,“Admin_default”路由与请求不匹配应该是有意义的。因此,按照设计,MVC 框架会尝试将请求与任何其他已定义的路由进行匹配。如果您使用 Visual Studio 中的 MVC 工具创建 Web 项目,您将在“Global.asax”文件(位于 Web 项目的根目录)中定义一个“默认”路由。它应该类似于:

// Web/Global.asax.cs

public static void RegisterRoutes(RouteCollection routes) {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
    );
}

“Default”路由成功匹配“ http://website/Abstract ”请求,“controller”=“Abstract”,“action”=“Index”(默认值),“id”=UrlParameter.Optional (默认值)。这是正确的、有意的行为……到目前为止。

现在,MVC 框架将尝试加载“抽象”控制器。按照设计,MVC 将搜索名为“AbstractController”的类,该类在 Web 项目的文件/命名空间层次结构中的任何位置扩展“Controller”。需要注意的是,Controller 的文件位置和命名空间不会影响 MVC 找到它的能力;换句话说,仅仅因为您将“AbstractController”放在名为“Areas\Admin\Controllers”的文件夹中,并将命名空间更改为“Web.Areas.Admin.Controllers”而不是“Web.Controllers” , 并不意味着 MVC 不会使用它。

当 MVC 在“AbstractController”中执行“Index”操作时,很可能只返回“View()”,然后 MVC 会感到困惑,因为它不知道在哪里可以找到“Index”视图。因为 MVC 匹配了一个非区域路由(Global.asax 中的“默认”路由),它认为匹配的视图应该位于非区域视图文件夹中。因此,您会收到熟悉的错误消息:

The view 'Index' or its master was not found. The following locations were searched:
~/Views/Abstract/Index.aspx
~/Views/Abstract/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx

我们和您一样,不希望对“ http://website/Abstract ”的请求解析为“Admin”区域的“AbstractController”;只有“ http://website/Admin/Abstract ”应该有效。我想不出为什么有人会想要这种行为。

一个简单的解决方案是删除 Global.asax 中的“默认”路由,但这会破坏任何常规的非区域控制器/视图。对于大多数人来说,这可能不是一个选择......

因此,我们认为我们可以限制 MVC 用于与 Global.asax 中的“默认”路由匹配的请求的控制器集:

// Web/Global.asax.cs

public static void RegisterRoutes(RouteCollection routes) {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new {controller = "Home", action = "Index", id = UrlParameter.Optional},
        new[] {"Web.Controllers"} // Added this line
    );
}

没有。即使“ AbstractController ”的命名空间是“Web.Areas.Admin.Controllers”并且(显然)不是“ Web.Controllers”。这完全令人困惑;看起来这个白名单对 MVC 的控制器分辨率没有明显的影响。

- tl;博士的答案从这里开始 -

经过一些黑客攻击,我们想出了如何强制 MVC 只使用白名单命名空间中的控制器。

// Web/Global.asax.cs

public static void RegisterRoutes(RouteCollection routes) {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new {controller = "Home", action = "Index", id = UrlParameter.Optional},
        new[] {"Web.Controllers"}
    ).DataTokens["UseNamespaceFallback"] = false; // Added this line
}

将“默认”路由上的 DataTokens 字典的“UseNamespaceFallback”键设置为 false。现在,当我们向“ http://website/Abstract ”发出请求时,“默认”路由仍将匹配(这是有效行为!)但 MVC不会使用不在定义的命名空间内的任何控制器(s ); 在这种情况下,只有“Web.Controllers”命名空间中的控制器是有效的。最后,这是我们正在寻找的功能!我们无法弄清楚为什么这不是默认行为。很奇怪吧?

希望这可以帮助。

于 2010-05-06T23:05:47.533 回答
1

您是否正确设置了路由?当您使用区域时,您必须手动更改您的路由代码,以便 MVC 在正确的命名空间中查找。

http://haacked.com/archive/2010/01/12/ambiguous-controller-names.aspx

于 2010-02-23T08:32:29.760 回答