我和我的朋友在使用 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”命名空间中的控制器是有效的。最后,这是我们正在寻找的功能!我们无法弄清楚为什么这不是默认行为。很奇怪吧?
希望这可以帮助。