ASP.NET MVC Html.ActionLink维护路由值

时间:2010-04-16 08:48:53

标签: c# asp.net-mvc model-view-controller asp.net-mvc-2

我有一个问题,在这里几乎被问到:

asp.net mvc Html.ActionLink() keeping route value I don't want

然而,最终的解决方案是纯粹而简单的kludge,我真的很想知道为什么会发生这种情况,如果有人可以向我解释一下呢?

为了完整起见,可以非常轻松地重新创建场景:

  • 创建一个新的MVC Web应用程序。
  • 跑吧。
  • 访问“关于”选项卡修改要读取的URL /主页/关于/ Flib - 这显然会引导您使用我们不关心的ID为“Flib”的操作。

请注意,现在关于About的顶级菜单链接实际上链接到/ Home / About / Flib - 据我所知这是错误的,因为我现在绝对没有办法使用站点链接返回/ Home /约

我真的不明白为什么我应该被强制修改我的所有Html.ActionLinks以包含new { id = string.Empty }用于路由值,而为htmlAttribs包含null。这似乎特别糟糕,因为我已经指定id = 0作为路线本身的一部分。

希望我在这里错过了一个技巧。

3 个答案:

答案 0 :(得分: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,这只是匹配的路径,在这种情况下恰好是默认路由。

答案 1 :(得分:3)

如果我理解正确,听起来您需要注册第二个“仅限操作”路线并使用Html.RouteLink。首先在应用程序启动时注册这样的路由:

routes.MapRoute("ActionOnly", "{controller}/{action}", new { controller = "Home", action = "Index" } );

然后使用以下代替ActionLink来创建这些链接:

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

答案 2 :(得分:1)

如果您不知道需要显式覆盖哪些值,或者您只是想避免额外的参数列表,您可以使用如下所示的扩展方法。

<a href="@Url.Isolate(u => u.Action("View", "Person"))">View</a>

实施细节为in this blog post