当没有指定区域时,ASP.NET MVC 2 RC 2返回特定于区域的控制器

时间:2010-02-22 21:53:27

标签: asp.net-mvc-2 routes areas

我有一个带有一个基本级控制器(“Home”)的基本MVC 2(RC2)站点,以及一个带有一个控制器(“Abstract”)的区域(“Admin”)。当我调用http://website/Abstract时 - 即使我没有在URL中指定区域,也会调用Admin区域中的Abstract控制器。更糟糕的是 - 它似乎不知道它在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

我做错了吗?这是一个错误吗?功能?

2 个答案:

答案 0 :(得分:11)

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

您可能在“管理员”区域的“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}
    );
}

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

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

当MVC在“AbstractController”中执行“索引”操作时,很可能只返回“View()”,然后MVC会因为不知道在哪里找到“索引”视图而感到困惑。因为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
    );
}

不。对“http://website/Abstract”的请求仍然在“Admin”区域内使用“AbstractController”,即使“AbstractController”的命名空间是“Web.Areas.Admin.Controllers”并且(显然)不是“Web.Controllers”。这完全令人困惑;看起来这个白名单对MVC的Controller分辨率没有可决定的影响。

- 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将使用任何不在内的控制器定义的命名空间;在这种情况下,只有“Web.Controllers”命名空间中的控制器才有效。最后,这是我们正在寻找的功能!我们无法弄清楚为什么这不是默认行为。很奇怪,是吗?

希望这有帮助。

答案 1 :(得分:1)

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

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