ASP.NET MVC Html.ActionLink 保留路由值

12

我有一个问题,这个问题在这里已经被问过了:

asp.net mvc Html.ActionLink() 如何保留不需要的路由值

然而,最终解决方案是一种不太优雅的方法,我真的希望能够理解为什么会这样,如果有人能够解释一下,那就太好了?

为了完整起见,可以很容易地重新创建场景:

  • 创建一个新的MVC Web应用程序。
  • 运行它。
  • 访问“关于”选项卡。将URL修改为/Home/About/Flib - 这显然会将您带到具有“Flib” id的操作,但我们并不关心这些。

请注意,顶部菜单链接到“关于”,现在实际上链接到/Home/About/Flib - 在我看来,这是错误的,因为我现在完全没有办法使用站点链接返回/Home/About.

我真的不明白为什么我应该被迫修改所有Html.ActionLinks,以包括new { id = string.Empty } 作为路由值和null作为htmlAttribs。这似乎特别不协调,因为我已经在路由本身中指定了id = 0.

希望我在这里没有错过什么诀窍。

3个回答

13

当您查看操作链接的源代码时,您会发现

<%= Html.ActionLink("LinkText", "Action", "Controller"); %>

将匹配

public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName) {
        return ActionLink(htmlHelper, linkText, actionName, controllerName, new RouteValueDictionary(), new RouteValueDictionary());
    }

到目前为止,这看起来很好,因为它正在创建一个新的路由值字典,所以它不会传递当前上下文中的值以添加到新链接中,这就是将发生的事情。

然而,在代码的深处生成URL时:

public static string GenerateUrl(string routeName, string actionName, string controllerName, RouteValueDictionary routeValues, RouteCollection routeCollection, RequestContext requestContext, bool includeImplicitMvcValues) {
        if (routeCollection == null) {
            throw new ArgumentNullException("routeCollection");
        }

        if (requestContext == null) {
            throw new ArgumentNullException("requestContext");
        }

        RouteValueDictionary mergedRouteValues = RouteValuesHelpers.MergeRouteValues(actionName, controllerName, requestContext.RouteData.Values, routeValues, includeImplicitMvcValues);

        VirtualPathData vpd = routeCollection.GetVirtualPathForArea(requestContext, routeName, mergedRouteValues);
        if (vpd == null) {
            return null;
        }

        string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath);
        return modifiedUrl;
    }

您可以看到引用了requestContext对象,它可以访问routeData和routeCollections,其中将包含ID数据。在创建VirtualPathForArea时,以下行是ID值出现在URL中的位置:

internal static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, string name, RouteValueDictionary values, out bool usingAreas) {
        if (routes == null) {
            throw new ArgumentNullException("routes");
        }

        if (!String.IsNullOrEmpty(name)) {
            // the route name is a stronger qualifier than the area name, so just pipe it through
            usingAreas = false;
            return routes.GetVirtualPath(requestContext, name, values);
        }

        string targetArea = null;
        if (values != null) {
            object targetAreaRawValue;
            if (values.TryGetValue("area", out targetAreaRawValue)) {
                targetArea = targetAreaRawValue as string;
            }
            else {
                // set target area to current area
                if (requestContext != null) {
                    targetArea = AreaHelpers.GetAreaName(requestContext.RouteData);
                }
            }
        }

        // need to apply a correction to the RVD if areas are in use
        RouteValueDictionary correctedValues = values;
        RouteCollection filteredRoutes = FilterRouteCollectionByArea(routes, targetArea, out usingAreas);
        if (usingAreas) {
            correctedValues = new RouteValueDictionary(values);
            correctedValues.Remove("area");
        }

        VirtualPathData vpd = filteredRoutes.GetVirtualPath(requestContext, correctedValues);
        return vpd;
    }

这行代码:

VirtualPathData vpd = filteredRoutes.GetVirtualPath(requestContext, correctedValues);

获取虚拟路径(即路由)并将其返回。因此,虚拟路径将为/Home/About/Flib

然后,当返回该虚拟路径时,下一行将使用它来设置操作链接的客户端URL:

 string modifiedUrl = PathHelpers.GenerateClientUrl(requestContext.HttpContext, vpd.VirtualPath);

看起来 ActionLink 是使用区域的虚拟路径设置的,这个虚拟路径就是与默认路由匹配的路由。


5
这是否解决了问题,还是仅仅是对症状的解释,无法得到充分的解决? - Robert Koritnik
@Robert Koritnik - 这只是对ASP.NET MVC 2中正在发生的事情的解释。不确定这段代码是否在ASP.NET MVC 3中更改了。 - amurra
我不知道,而且说实话我也不太在意,因为我也在使用MVC2 RTM(因为我必须使用.NET 3.5)。所以我也遇到了这个问题。但是我做了以下操作,似乎可以解决:https://dev59.com/qE_Ta4cB1Zd3GeqPDsA1#5110032 - Robert Koritnik

3
如果我理解正确的话,您需要注册第二个“仅操作”路由并使用Html.RouteLink。首先,在应用程序的启动中注册一个像这样的路由:
routes.MapRoute("ActionOnly", "{controller}/{action}", new { controller = "Home", action = "Index" } );

那么,不要使用ActionLink来创建这些链接,而是使用:

Html.RouteLink("About","ActionOnly")

1
如果你不知道哪些值需要明确覆盖,或者只是想避免额外的参数列表,可以使用下面这个扩展方法。
<a href="@Url.Isolate(u => u.Action("View", "Person"))">View</a>

实现细节可以在这篇博客文章中找到。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接