ASP。净MVC Url。Action将当前路由值添加到生成的url。

时间:2022-11-30 21:17:55

I have seen this question a couple of times here in SO but none of them with any acceptable answer:

我在这里见过几次这样的问题,但没有一个可以接受的回答:

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

ASP。网MVC。操作包括当前路由数据ASP。NET MVC隐式地添加了路由值。

Basically I have Controller with an action method called Group, it has an overload that receives no parameters and displays a list of elements and another one that receives an id and displays details for that group.

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

If I do something like this:

如果我这样做:

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

From the main page of the site (/) it returns an url like this:

从站点的主页(/)返回如下url:

"mysite.com/Groups/Group"

which is alright Now, if the current address of the site is /Groups/Group/1 And I call the same method

现在,如果站点的当前地址是/Groups/Group/1,而我调用相同的方法,这是正确的吗

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

the returned url is this:

返回的url是:

"mysite.com/Groups/Group/1"

It automatically adds the value of the route for the current page when generating the URL. Even if I generate the URL this way:

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

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

Thus explicitly specifying that I don't want any route values, the generated URL is the same. To get the address I want I have to explicitly set the route value to an empty string, like so:

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

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

This will generate the following url:

这将生成以下url:

"mysite.com/Groups/Group"

My question is, why does this happen? If I don't set any route values it shouldn't add them to the generated URL.

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

7 个解决方案

#1


48  

Url.Action will reuse the current request parameters, if you do not explicitly set them. It is by design in outbound url-matching algorithm. When looking for the route data parameters in a process of generating url, parameters are taken from:

Url。如果您没有显式地设置它们,操作将重用当前的请求参数。它是设计在出站url匹配算法。在生成url的过程中查找路由数据参数时,从:

1) explicitly provided values

1)显式地提供值

2) values from the current request

2)当前请求的值

3) defaults

3)违约

In the order I specified above.

按照我上面指定的顺序。

Outbound matching algorithm for routes is complicated, so it is good practice to explicitly set all parameters for request, as you did in your example

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

#2


2  

My application explicitly sets route values and does not want a magical value from the current request. I want to be in full control.

我的应用程序显式地设置了路由值,并且不希望从当前请求中获得一个神奇的值。我想完全控制自己。

I have made an extension which coexists with my route library collection. Hence the single RouteValueDictionary param. (See my Route library comment at the bottom)

我已经做了一个扩展,它与我的路线库集合共存。因此,只有一个RouteValueDictionary param。(见我的路径库注释在底部)

Here I remove any routevalues from the request prior to generating a url.

在这里,我在生成url之前从请求中删除任何routevalues。

(note: for the array.contains ignorecase part, see: 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);
    }
}

I have Form and ActionLink extension methods that uses the RemoveRoutes method. No helper in my mvc library uses a method that is not an extension method i have created. Thereby, all routedata is cleaned up before generating urls.

我有使用RemoveRoutes方法的表单和ActionLink扩展方法。我的mvc库中没有任何助手使用的方法不是我创建的扩展方法。因此,在生成url之前,所有的routedata都被清除了。

For reference I use AttributeRouting. Here is an example of one route from my route library.

作为参考,我使用属性路由。这里有一个从我的路线库中走的路线的例子。

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

#3


2  

So when I read objectbox's answer I thought I would have to modify several links in my code. I then tried adding a default route omitting the default parameters which solved the problem:

所以当我阅读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+" }
);

#4


1  

Simple example:

简单的例子:

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

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

Edit view contains only this:

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

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

So when you run your site e.g. localhost:randomPort/Product/Edit/123 you get next response: /Product/Detail/123

因此,当您运行您的站点时,例如localhost:randomPort/Product/Edit/123,您将得到下一个响应:/Product/Detail/123

Why? Because Route attribute has required parameter id. Id parameter is read from url, although we wrote only Url.Action(methodName, controller) - without specifying parameter. Also it doesn't make sense to have a method detail without id.

为什么?因为Route属性需要参数id。id参数是从url读取的,尽管我们只编写url。Action(methodName, controller)——不指定参数。而且没有id的方法细节也没有意义。

In order for attributes to work next line must be added to RouteConfig.cs:

为了使属性起作用,必须将下一行添加到RouteConfig.cs:

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

#5


0  

A simple workaround would be first to call

首先要做的是找一个简单的变通办法

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

and then rename dummy in the resulting string to the correct action name.

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

#6


0  

A silly workaround I found that works to handle the current page routing data, including changing the page parameter upon your preferences

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

@{
    var current_route_1 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);
    var current_route_2 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);

    //If you want to customize the routing values
    current_route_1["controller"] = "Controller1";
    current_route_2["controller"] = "Controller2";
}

@Url.RouteUrl(current_route_1);
@Url.RouteUrl(current_route_2);

#7


0  

Here's a workaround that doesn't require attribute routing, nor does it alter the current route values within your request. This idea comes from http://notherdev.blogspot.ca/2013/10/aspnet-mvc-current-values-used-by-url-action.html, which I altered slightly to work without MvcFutures.

这里有一个解决方案,它不需要属性路由,也不更改请求中的当前路由值。这个想法来自http://notherdev.blogspot./201ca3/10/aspnet -mvc-current- valued -used by url-action。html,在没有MvcFutures的情况下,我稍微修改了一下。

I built my own Action extension on UrlHelper with an additional bool that allows you to optionally ignore the current route values (as to not * with the existing Action methods on UrlHelper.)

我在UrlHelper上构建了自己的操作扩展,并添加了一个bool,允许您随意地忽略当前的路由值(以免与UrlHelper上的现有操作方法发生冲突)。

What it does is builds a completely new RequestContext from the current one, by using the current route but not the current route values. Then we pass that along to the base helpers. That way, when the base helpers go to look at what the route values are from within the request context, it'll find none, and thus not use them when generating the URL.

它所做的是使用当前的路由而不是当前的路由值构建一个全新的请求上下文。然后我们把它传递给辅助基。这样,当基本的helper函数查看请求上下文中的路由值时,它会找到none,因此在生成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);
}

#1


48  

Url.Action will reuse the current request parameters, if you do not explicitly set them. It is by design in outbound url-matching algorithm. When looking for the route data parameters in a process of generating url, parameters are taken from:

Url。如果您没有显式地设置它们,操作将重用当前的请求参数。它是设计在出站url匹配算法。在生成url的过程中查找路由数据参数时,从:

1) explicitly provided values

1)显式地提供值

2) values from the current request

2)当前请求的值

3) defaults

3)违约

In the order I specified above.

按照我上面指定的顺序。

Outbound matching algorithm for routes is complicated, so it is good practice to explicitly set all parameters for request, as you did in your example

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

#2


2  

My application explicitly sets route values and does not want a magical value from the current request. I want to be in full control.

我的应用程序显式地设置了路由值,并且不希望从当前请求中获得一个神奇的值。我想完全控制自己。

I have made an extension which coexists with my route library collection. Hence the single RouteValueDictionary param. (See my Route library comment at the bottom)

我已经做了一个扩展,它与我的路线库集合共存。因此,只有一个RouteValueDictionary param。(见我的路径库注释在底部)

Here I remove any routevalues from the request prior to generating a url.

在这里,我在生成url之前从请求中删除任何routevalues。

(note: for the array.contains ignorecase part, see: 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);
    }
}

I have Form and ActionLink extension methods that uses the RemoveRoutes method. No helper in my mvc library uses a method that is not an extension method i have created. Thereby, all routedata is cleaned up before generating urls.

我有使用RemoveRoutes方法的表单和ActionLink扩展方法。我的mvc库中没有任何助手使用的方法不是我创建的扩展方法。因此,在生成url之前,所有的routedata都被清除了。

For reference I use AttributeRouting. Here is an example of one route from my route library.

作为参考,我使用属性路由。这里有一个从我的路线库中走的路线的例子。

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

#3


2  

So when I read objectbox's answer I thought I would have to modify several links in my code. I then tried adding a default route omitting the default parameters which solved the problem:

所以当我阅读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+" }
);

#4


1  

Simple example:

简单的例子:

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

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

Edit view contains only this:

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

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

So when you run your site e.g. localhost:randomPort/Product/Edit/123 you get next response: /Product/Detail/123

因此,当您运行您的站点时,例如localhost:randomPort/Product/Edit/123,您将得到下一个响应:/Product/Detail/123

Why? Because Route attribute has required parameter id. Id parameter is read from url, although we wrote only Url.Action(methodName, controller) - without specifying parameter. Also it doesn't make sense to have a method detail without id.

为什么?因为Route属性需要参数id。id参数是从url读取的,尽管我们只编写url。Action(methodName, controller)——不指定参数。而且没有id的方法细节也没有意义。

In order for attributes to work next line must be added to RouteConfig.cs:

为了使属性起作用,必须将下一行添加到RouteConfig.cs:

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

#5


0  

A simple workaround would be first to call

首先要做的是找一个简单的变通办法

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

and then rename dummy in the resulting string to the correct action name.

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

#6


0  

A silly workaround I found that works to handle the current page routing data, including changing the page parameter upon your preferences

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

@{
    var current_route_1 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);
    var current_route_2 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);

    //If you want to customize the routing values
    current_route_1["controller"] = "Controller1";
    current_route_2["controller"] = "Controller2";
}

@Url.RouteUrl(current_route_1);
@Url.RouteUrl(current_route_2);

#7


0  

Here's a workaround that doesn't require attribute routing, nor does it alter the current route values within your request. This idea comes from http://notherdev.blogspot.ca/2013/10/aspnet-mvc-current-values-used-by-url-action.html, which I altered slightly to work without MvcFutures.

这里有一个解决方案,它不需要属性路由,也不更改请求中的当前路由值。这个想法来自http://notherdev.blogspot./201ca3/10/aspnet -mvc-current- valued -used by url-action。html,在没有MvcFutures的情况下,我稍微修改了一下。

I built my own Action extension on UrlHelper with an additional bool that allows you to optionally ignore the current route values (as to not * with the existing Action methods on UrlHelper.)

我在UrlHelper上构建了自己的操作扩展,并添加了一个bool,允许您随意地忽略当前的路由值(以免与UrlHelper上的现有操作方法发生冲突)。

What it does is builds a completely new RequestContext from the current one, by using the current route but not the current route values. Then we pass that along to the base helpers. That way, when the base helpers go to look at what the route values are from within the request context, it'll find none, and thus not use them when generating the URL.

它所做的是使用当前的路由而不是当前的路由值构建一个全新的请求上下文。然后我们把它传递给辅助基。这样,当基本的helper函数查看请求上下文中的路由值时,它会找到none,因此在生成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);
}