【问题标题】:@Url.RouteUrl returns misleading/wrong path for a controller-action (unless the 'routeName' parameter is explicitly specified)@Url.RouteUrl 返回控制器操作的误导/错误路径(除非明确指定了“routeName”参数)
【发布时间】:2017-08-14 13:01:41
【问题描述】:

假设我们在 MVC4 项目中有两个控制器 ControllerA 和 ControllerB,如下所示:

  Controllers
  |
  \- MvcApi
  |   |
  |   \- ControllerB.cs
  |
  \- ControllerA.cs

这个概念是 ControllerB 是一个 mvc 控制器,但是它纯粹通过 ajax 调用来提供 html-razor-partial-views。另一方面,ControllerA 是一个接地气的 mvc 控制器。在 RouteConfig.cs 我们有:

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

        routes.MapRoute(
            name: "MvcApi",
            url: "mvcapi/{controller}/{action}/{id}",
            defaults: new { id = UrlParameter.Optional },
            namespaces: new[] { typeof(BController).Namespace } //<- "Project.Views.MvcApi"
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "AController", action = "Index", id = UrlParameter.Optional },
            namespaces: new[] { typeof(AController).Namespace } //<- "Project.Views"
        );
    }
}

我们现在尝试自动生成一个指向 AController 动作的 url,如下所示:

 @(Url.RouteUrl(routeValues: new { controller = "A", action = nameof(AController.SomeActionNameHere) }))

鉴于上面显示的配置,我们预计上面的语句会导致:

"/A/SomeActionNameHere"

然而我们得到的是:

"/mvcapi/A/SomeActionNameHere"

这当然在运行时有效,但它完全具有误导性(“仅仅因为它在工作并不意味着它是正确的”)。我们可以通过明确指定路由名称来强制执行所需的行为:

@(Url.RouteUrl(
     routeName: "Default",  // <- this "fixes" the issue
     routeValues: new { controller = "A", action = nameof(AController.SomeActionNameHere) })
) 

这确实返回了所需的网址:

"/A/SomeActionNameHere"

然而,我对 asp.net 基础设施如此容易堵塞并最终需要如此多的“手持”才能确定 AController 直接位于“Views”命名空间下而不是在“Views.MvcApi”下。人们会期望发出一个简单的:

 @(Url.RouteUrl(routeValues: new { controller = "A", action = nameof(AController.SomeActionNameHere) }))

会立即自动神奇地返回正确的网址。怎么回事?

附录:我还检查了项目的 2 个 web.config 文件,没有发现任何可疑之处。如果您认为我可能有什么问题,我会用所需的 sn-ps 更新这篇文章,供您检查。

【问题讨论】:

    标签: c# asp.net asp.net-mvc razor routes


    【解决方案1】:

    每个构建 url 的请求都会经过定义路由的集合,并使用第一个满足要求的路由。您已经定义了两条路线。它们都需要 {controller}、{action} 和 {id},这正是您提供的(ID 是可选的)。在此过程中,它不会检查控制器或操作是否存在,您有责任确保您的映射不会创建不存在的 url。由于您的 MvcApi 路由是列表中的第一个,因此 MVC 使用此定义为您构建 URL。

    为了避免这种行为,您可以完全省略 {controller} 参数并将其更改为:

    routes.MapRoute(
        name: "MvcApi",
        url: "mvcapi/BController/{action}/{id}",
        defaults: new { id = UrlParameter.Optional },
        namespaces: new[] { typeof(BController).Namespace } //<- "Project.Views.MvcApi"
    );
    
    routes.MapRoute(
        name: "Default",
        url: "AController/{action}/{id}",
        defaults: new { action = "Index", id = UrlParameter.Optional },
        namespaces: new[] { typeof(AController).Namespace } //<- "Project.Views"
    );
    

    或者例如使用约束

    routes.MapRoute(
        name: "Default",
        url: "AController/{action}/{id}",
        defaults: new { action = "Index", id = UrlParameter.Optional },
        constraints: new { controller = @"(BController|SomeOtherControllerName)" },
        namespaces: new[] { typeof(AController).Namespace } //<- "Project.Views"
    );
    

    这将帮助您为路线添加更多规则。

    如果有帮助,您还可以使用映射路线的顺序。

    或者您可以继续使用路线的名称。这是告诉 MVC 你要呈现哪个 URL 的绝对有效的方法。

    【讨论】:

    • 感谢您的调整。我知道这种方法,但我无法接受每次在 MvcApi 文件夹下添加新控制器时都必须手动添加新路由的事实。这真的是 21 世纪技术所能做到的最好的吗? :)
    • 并非如此。您不必为每个控制器创建新路由。但是您构建路线的方式取决于很多事情。每个项目都是独一无二的,这就是为什么您有多种方法来映射路线以及如何区分它们 - 名称、参数、约束甚至映射路线的顺序。
    • 您是否愿意通过一个示例来更新您的响应,该示例说明如何自动定位一个名为 CController 的控制器,该控制器与 BController 位于同一文件夹+命名空间中?如果不引入另一个单独的(显式)路由,如何实现这一点?这是拼图中缺少的部分。
    • 这就是“默认”路由的作用。它将处理所有控制器,但您只需要确保在您需要更具体的东西时不会选择错误的路线。就像您的案例中的“MvcApi”路线一样。所以正如我建议的那样:使用“约束”:正则表达式为每个路由指定有效的控制器名称。或者 - 如果 MvcApi 路由只有一个控制器 - 在映射时使用显式路由。但保持“默认”路线不变。或者 - 恕我直言,更好的做法 - 使用 Url.RouteUrl 而不是 Url.Action。像@Url.RouteUrl("MvcApi", new { controller = "Home", action = "Index" });
    • 我想如果我使用约束和一些反射魔法来动态创建约束正则表达式“在服务器启动时”,我可以制作一条通用路由。我会让这个问题停留一段时间,希望能有更多关于这个问题的输入/反馈。
    猜你喜欢
    • 2014-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多