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

发布于 2024-08-22 11:55:10 字数 493 浏览 5 评论 0 原文

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

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

我做错了什么吗?这是一个错误吗?一个特点?

I have a basic MVC 2 (RC2) site with one base-level controller ("Home"), and one area ("Admin") with one controller ("Abstract"). When i call http://website/Abstract - the Abstract controller in the Admin area gets called even though i haven't specified the Area in the URL. To make matters worse - it doesn't seem to know it's under Admin because it can't find the associated view and just returns:

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

Am i doing something wrong? Is this a bug? A feature?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

刘备忘录 2024-08-29 11:55:10

我和我的朋友在 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}
    );
}

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

现在,MVC 框架将尝试加载“抽象”控制器。根据设计,MVC 将搜索名为“AbstractController”的类,该类将“Controller”扩展至 Web 项目的文件/命名空间层次结构中的任何位置。需要注意的是,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
    );
}

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

- tl;dr 答案从这里开始 -

经过一些黑客攻击,我们想出了如何强制 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
}

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

希望这有帮助。

My friend and I were experiencing the same issue with Areas in ASP.NET MVC 2. We found a "hack" that, so far, seems to be working. For the tl;dr version, see the bottom of this answer.

You've probably got something similar to the following in your "Admin" area's "AdminAreaRegistration.cs" class:

// 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 }
    );
}

Thus, it should make sense that when you make a request for "http://website/Abstract", the "Admin_default" route does not match the request. So, by design, the MVC framework attempts to match the request against any other defined routes. If you used the MVC tooling within Visual Studio to create your web project, you'll have a "Default" route defined in your "Global.asax" file (at the root of your web project). It should look similar to this:

// 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}
    );
}

The "Default" route succeeds in matching the request for "http://website/Abstract", with "controller" = "Abstract", "action" = "Index" (default value), and "id" = UrlParameter.Optional (default value). This is the correct, and intended, behavior... so far.

Now, the MVC framework will attempt to load the "Abstract" Controller. By design, MVC will search for a class called "AbstractController" that extends "Controller" anywhere within the web project's file/namespace hierarchy. It is important to note that a Controller's file location and namespace do not affect MVC's ability to find it; in other words, just because you've placed the "AbstractController" within a folder called "Areas\Admin\Controllers" and changed the namespace to be "Web.Areas.Admin.Controllers" instead of, say, "Web.Controllers", doesn't mean that MVC won't use it.

When MVC executes the "Index" action in "AbstractController" which, most likely, just returns "View()", then MVC gets confused because it doesn't know where to find the "Index" view. Because MVC has matched a non-area route (the "Default" route in Global.asax), it thinks the matching view should be located in non-area view folders. Thus you get the familiar error message:

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

We, just as you, didn't want requests for "http://website/Abstract" to resolve to "Admin" area's "AbstractController"; only "http://website/Admin/Abstract" should work. I can't think of why anyone would want this behavior.

A simple solution is to delete the "Default" route in Global.asax, but this will break any regular non-area Controllers/Views. This is probably not an option for most people...

So, we thought we could restrict the set of Controllers that MVC would use for requests matched by the "Default" route in 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
    );
}

Nope. A request for "http://website/Abstract" will still use "AbstractController" within the "Admin" area, even though the "AbstractController"'s namespace is "Web.Areas.Admin.Controllers" and (clearly) not "Web.Controllers". This is thoroughly confusing; it seems like this white-list has no dicernable affect on MVC's Controller resolution.

- tl;dr answer starts here -

After some hacking, we figured out how to force MVC to only use Controllers within the white-listed namespace(s).

// 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
}

Set the "UseNamespaceFallback" key of the DataTokens dictionary on the "Default" route to false. Now, when we make a request for "http://website/Abstract", the "Default" route will still be matched (this is valid behavior!) but MVC will not use any Controller that is not within the defined namespace(s); in this case, only Controllers within the "Web.Controllers" namespace are valid. Finally, this is the functionality we were looking for! We can't figure out why this isn't the default behavior. Weird, huh?

Hope this helps.

一萌ing 2024-08-29 11:55:10

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

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

Did you setup your routing correctly? When you use areas you have to manually change your routing code so that MVC looks in the right namespaces.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文