ASP.NET MVC Url.Action将当前路由值添加到生成的URL

时间:2011-08-20 16:52:27

标签: asp.net-mvc url model-view-controller routing

我已经在SO中多次看过这个问题,但没有一个问题可以接受:

ASP.NET MVC @Url.Action includes current route data
ASP.NET MVC implicitly adds route values

基本上我有一个名为Group的动作方法的Controller,它有一个不接收任何参数的重载,显示一个元素列表,另一个接收一个id并显示该组的详细信息。

如果我这样做:

Url.Action("Group", "Groups");

从网站的主页面(/)返回一个这样的网址:

"mysite.com/Groups/Group"

没关系 现在,如果站点的当前地址是/ Groups / Group / 1 我称之为相同的方法

Url.Action("Group", "Groups");

返回的网址是:

"mysite.com/Groups/Group/1"

生成URL时,它会自动为当前页面添加路由值。 即使我这样生成URL:

Url.Action("Group", "Groups", null);

因此明确指定我不想要任何路由值,生成的URL是相同的。 要获取我想要的地址,我必须将路由值显式设置为空字符串,如下所示:

Url.Action("Group", "Groups", new {id=""});

这将生成以下网址:

"mysite.com/Groups/Group"

我的问题是,为什么会这样?如果我没有设置任何路由值,则不应将它们添加到生成的URL中。

7 个答案:

答案 0 :(得分:50)

如果您没有明确设置它们,Url.Action将重用当前的请求参数。它是出站网址匹配算法的设计。在生成URL的过程中查找路径数据参数时,参数取自:

1)明确提供值

2)来自当前请求的值

3)默认

按照我上面指定的顺序。

路由的出站匹配算法很复杂,因此最好为请求显式设置所有参数,就像在示例中所做的那样

答案 1 :(得分:2)

我的应用程序显式设置路由值,不需要当前请求的神奇值。我希望完全掌控。

我制作了一个与我的路线库集合共存的扩展程序。 因此单个RouteValueDictionary参数。 (请参阅底部的路线库评论)

在此,我会在生成网址之前从请求中删除所有路由值。

(注意:对于array.contains ignorecase部分,请参阅:How can I make Array.Contains case-insensitive on a string array?

public static string Action(this UrlHelper helper, 
                            RouteValueDictionary routeValues)
{
    RemoveRoutes(helper.RequestContext.RouteData.Values);

    string url = helper.Action(routeValues["Action"].ToString(), routeValues);
    return url;
}

public static void RemoveRoutes(RouteValueDictionary currentRouteData)
{
    List<string> keyList = new List<string>(currentRouteData.Keys);

    string[] ignore = new[] { "Area", "Controller", "Action" };
    foreach (string key in keyList)
    {
        if (!ignore.Contains(key, StringComparer.CurrentCultureIgnoreCase))
            currentRouteData.Remove(key);
    }
}

我有使用RemoveRoutes方法的Form和ActionLink扩展方法。我的mvc库中没有帮助器使用的方法不是我创建的扩展方法。因此,在生成URL之前清理所有routedata。

作为参考,我使用AttributeRouting。以下是路线库中一条路线的示例。

public static RouteValueDictionary DisplayNews(int newsId)
{
    RouteValueDictionary route = new RouteValueDictionary();
    route["Area"] = _area;
    route["Controller"] = _controller;
    route["Action"] = "DisplayNews";
    route["newsId"] = newsId;
    return route;
}

答案 2 :(得分:2)

所以当我读到objectbox的答案时,我想我必须修改代码中的几个链接。然后我尝试添加一个默认路由,省略了解决问题的默认参数:

routes.MapRoute(
    "ArtistArtworkDefPage",
    "Artist/{username}/Artwork",
    new
    {
        controller = "Artist",
        action = "Artwork",
        page = 1
    }
);

routes.MapRoute(
    "ArtistArtwork",
    "Artist/{username}/Artwork/{page}",
    new
    {
        controller = "Artist",
        action = "Artwork",
        page = 1
    },
    new { page = @"\d+" }
);

答案 3 :(得分:2)

简单示例:

public class ProductController : Controller
{
  public ActionResult Edit(int id)
  {
    return View();
  }

  [Route("Product/Detail/{id:int}")]
  public ActionResult Detail(int id)
  {
    return View();
  }
}

编辑视图仅包含以下内容:

@{ Layout = null;}
@Url.Action("Detail", "Cmr")

因此,当您运营您的网站时, localhost:randomPort/Product/Edit/123您得到下一个回复:/Product/Detail/123

为什么呢?因为Route属性需要参数id。 Id参数是从url读取的,尽管我们只写了Url.Action(methodName, controller) - 没有指定参数。另外,没有id的方法细节没有意义。

为了使下一行的属性工作,必须添加到RouteConfig.cs

public static void RegisterRoutes(RouteCollection routes)
{
  ...
  routes.MapMvcAttributeRoutes();
  ...
}

答案 4 :(得分:1)

这是一种不需要属性路由的解决方法,也不会改变请求中的当前路由值。这个想法来自http://notherdev.blogspot.ca/2013/10/aspnet-mvc-current-values-used-by-url-action.html,我在没有MvcFutures的情况下略微改变了。

我在UrlHelper上构建了我自己的Action扩展程序,其中包含一个额外的bool,允许您选择忽略当前路由值(以免与UrlHelper上的现有Action方法冲突。)

它的作用是使用当前路由从当前路由构建一个全新的RequestContext,但当前路由值。然后我们将它传递给基础助手。这样,当基本帮助者在请求上下文中查看路由值时,它将找不到,因此在生成URL时不会使用它们。

public static string Action(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues, bool ignoreCurrentRouteValues=false) {
    var routeValueDictionary = new RouteValueDictionary(routeValues);
    var requestContext = urlHelper.RequestContext;
    if (ignoreCurrentRouteValues) {
        var currentRouteData = requestContext.RouteData;
        var newRouteData = new RouteData(currentRouteData.Route, currentRouteData.RouteHandler);
        requestContext = new RequestContext(requestContext.HttpContext, newRouteData);
    }

    return UrlHelper.GenerateUrl(null, actionName, controllerName, routeValueDictionary,
        urlHelper.RouteCollection, requestContext, includeImplicitMvcValues: false);
}

答案 5 :(得分:0)

一个简单的解决方法是首先调用

Url.Action("dummy", new { ... }) 

然后将结果字符串中的dummy重命名为正确的操作名称。

答案 6 :(得分:0)

一个愚蠢的解决方法我发现它可以处理当前页面路由数据,包括根据您的偏好更改页面参数

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff"
    tools:context="sslwireless.com.testfullscreen.MainActivity">

    <!--RePresent Toolbar-->
    <LinearLayout
        android:orientation="horizontal"
        android:background="@drawable/gradient"
        android:layout_width="match_parent"
        android:layout_height="100dp">

        <ImageView
            android:layout_gravity="center_vertical"
            android:src="@drawable/ic_arrow_back_white_24dp"
            android:layout_marginLeft="10dp"
            android:layout_width="35dp"
            android:layout_height="35dp" />

        <TextView
            android:layout_gravity="center_vertical"
            android:textSize="25sp"
            android:gravity="center_horizontal"
            android:textColor="#fff"
            android:text="Test Title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>


</RelativeLayout>