ASP.NET MVC 2 RC 2 在未指定区域时返回特定于区域的控制器 [英] ASP.NET MVC 2 RC 2 returns Area-specific controller when no area specified

查看:15
本文介绍了ASP.NET MVC 2 RC 2 在未指定区域时返回特定于区域的控制器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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?

推荐答案

我和我的朋友在 ASP.NET MVC 2 中遇到了与区域相同的问题.我们发现了一个黑客",到目前为止,似乎是有效的.对于 tl;dr 版本,请参阅此答案的底部.

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.

您的管理"区域的AdminAreaRegistration.cs"类中可能有类似以下内容:

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

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

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

默认"路由成功将http://website/Abstract"的请求与controller" = "Abstract"、"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.

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

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.

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

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

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

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.

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

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...

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

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

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

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 答案从这里开始-

经过一些黑客攻击,我们想出了如何强制 MVC 仅使用列入白名单的命名空间内的控制器.

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
}

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

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?

希望这会有所帮助.

这篇关于ASP.NET MVC 2 RC 2 在未指定区域时返回特定于区域的控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆