如何防止Url.RouteUrl(...)从当前请求继承路由值

时间:2009-07-18 20:02:53

标签: asp.net-mvc-routing

假设您有一种在购物车中显示产品的操作方法

 // ProductsController.cs
 public ActionMethod Index(string gender) {

      // get all products for the gender
 }

在其他地方,在每个页面上显示的标头中,您使用Url.RouteUrl创建到网站上其他页面的HREF链接:

 <a href="<%= Url.RouteUrl("testimonials-route", new { }) %>" All Testimonials </a>

testimonials-routeglobal.ascx中通过以下第一条路线定义。 请注意,上面对RouteUrl的调用不提供gender,但路由的默认值为“中性”,因此我们希望调用Testimonials.Index(“中性”)。

 routes.MapRoute(
  "testimonials-route",
  "testimonials/{gender}",
  new { controller = "Testimonials", action = "Index", gender = "neutral" },
  new { gender = "(men|women|neutral)" }
 );

routes.MapRoute(
  "products-route",
  "products/{gender}",
  new { controller = "Products", action = "Index", gender = (string)null },
  new { gender = "(men|women|neutral)" }
 );

如果有人访问了该网页/products/women,我们就会获得HREF /testimonials/women 如果有人访问了页面/products,那么我们会得到一个空的HREF(对RouteUrl的调用返回null)。

但这没有意义吗?如果我们没有为它提供路由值,testimonials-route应该默认为'neutral'

事实证明,Url.RouteUrl(routeName, routeValues)帮助扩展程序将首先在其routeValues参数中查找gender路由值,如果它在该字典中找不到它,它将会查看我们当前的URL(请记住,Url是一个UrlHelper对象,它具有当前请求的上下文可用)。

如果我们在男性产品页面上,这可能会给我们提供男性推荐的链接,但如果我们没有通过RouteUrl调用中的值,这可能不是我们想要的,并在global.asax.cs文件中明确指定“中性”作为默认值。

如果我们访问了/products/,则会触发'products-route'路由并调用Products(null)方法。当我们使用Url.RouteUrl()创建网址时,对gender的调用实际上会继承testimonials-route的THIS空值。即使我们在gender'中指定了'testimionials-route的默认值,它仍然使用此空值,这会导致路由失败并且RouteUrl返回null。 [注意:路线失败,因为我们有一个约束(男人|女人|中立)而null不适合]

它实际上变得更加可怕 - 因为“控制器”和“动作”可以以相同的方式继承。即使在使用具有默认控制器的显式路由名称调用RouteUrl(...)时,这也可能导致URL生成完全错误的控制器。

在这种情况下,一旦你弄明白了,你可以通过多种方式轻松解决它,但在其他情况下可能会导致一些危险的行为。这可能是设计上的,但它绝对是可怕的。

2 个答案:

答案 0 :(得分:26)

我的解决方案是:

HtmlExtension助手方法:

    public static string RouteUrl(this UrlHelper urlHelper, string routeName, object routeValues, bool inheritRouteParams)
    {
        if (inheritRouteParams)
        {
            // call standard method
            return urlHelper.RouteUrl(routeName, routeValues);
        }
        else
        {
            // replace urlhelper with a new one that has no inherited route data
            urlHelper = new UrlHelper(new RequestContext(urlHelper.RequestContext.HttpContext, new RouteData()));
            return urlHelper.RouteUrl(routeName, routeValues);
        }
    }

我现在可以做到:

Url.RouteUrl('testimonials-route', new { }, false)

并且确定无论上下文如何,它总是会以相同的方式运行。

它的工作方式是取现有的UrlHelper并创建一个空白的'RouteData'。这意味着没有任何东西可以继承(甚至是空值)。

答案 1 :(得分:2)

也许我在这里遗漏了一些东西,但为什么不这样设置:

在您的全局asax中,创建两个路径:

routes.MapRoute(
    "testimonial-mf",
    "Testimonials/{gender}",
    new { controller = "Testimonials", action = "Index" }
);

routes.MapRoute(
    "testimonial-neutral",
    "Testimonials/Neutral",
    new { controller = "Testimonials", action = "Index", gender="Neutral" }
);

然后,使用Url.Action而不是Url.RouteUrl:

<%= Url.Action("Testimonials", "Index") %>
<%= Url.Action("Testimonials", "Index", new { gender = "Male" }) %>
<%= Url.Action("Testimonials", "Index", new { gender = "Female" }) %>

在我的机器上解析为以下网址:

/Testimonials/Neutral 
/Testimonials/Male 
/Testimonials/Female

我不确定这是否是一个可接受的解决方案,但它是另一种选择,不是吗?