html.ActionLink使用错误的路由

时间:2017-02-15 16:48:53

标签: asp.net asp.net-mvc routes

我已经设置了以下路由,以便可以使用重复的控制器名称(在不同的命名空间中)。这工作正常,但是当我从任何控制器使用html.actionlink时,它总是包含链接的“CRUD”子文件夹。

var route1 = routes.MapRoute(
    "CRUD",
    "CRUD/{controller}/{action}/{id}",
    new { action = "Index", id = UrlParameter.Optional }
);
route1.DataTokens["Namespaces"] = new string[] { "College.Controllers.CRUD" };
route1.DataTokens["UseNamespaceFallback"] = false;

var route2 = routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    namespaces: new[] { "College.Controllers" }
);

route2.DataTokens["Namespaces"] = new string[] { "College.Controllers" };
route2.DataTokens["UseNamespaceFallback"] = false;

所以http://localhost/students/index中的html.actionlink看起来像这样 http://localhost/CRUD/students/Edit/1

我想要的是这个 http://localhost/students/Edit/1

我知道我可以通过在actionlink中指定路由来解决这个问题,但我不想这样做,因为我想在将来重新构建并且我的更改将被覆盖。

1 个答案:

答案 0 :(得分:0)

这里的问题是,在构建URL时,您的2条路线不明确。基本上有3种方法可以解决这个问题:

  1. 添加另一个路线值以匹配不属于该网址的路径。
  2. 使用RouteLink按名称指定路线(以及其他路线值条件以使其匹配)。
  3. 创建自定义路由以处理将其“约束”到特定命名空间或制定自定义路由约束。
  4. 由于您明确声明第二个选项是不可接受的,因此以下是第一个选项的示例:

    var route1 = routes.MapRoute(
                   "CRUD",
                   "CRUD/{controller}/{action}/{id}",
                   new { crud = "crud", action = "Index", id = UrlParameter.Optional }
    
               );
        route1.DataTokens["Namespaces"] = new string[] { "College.Controllers.CRUD" };
        route1.DataTokens["UseNamespaceFallback"] = false;
    
    var route2 = routes.MapRoute(
                   "Default",
                   "{controller}/{action}/{id}",
                   new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    
               );
        route1.DataTokens["Namespaces"] = new string[] { "College.Controllers" };
        route1.DataTokens["UseNamespaceFallback"] = false;
    

    现在,当您致电@Html.ActionLink("Students", "Index", "Students", new { crud = (string)null }, null)时,它与CRUD路线不匹配,它将与Default路线匹配。

    要使其与CRUD路线匹配,您必须明确将路线值添加到ActionLink:@Html.ActionLink("Students", "Index", "Students", new { crud = "crud" }, null)或完全将其遗漏:@Html.ActionLink("Students", "Index", "Students")

    约束路线

    以下是第3个选项的示例。

    不幸的是,我们无法使用常规路由约束,因为Microsoft决定不在RequestContext接口中提供IRouteConstraint对象。这意味着关于请求绑定到哪个控制器的命名空间信息不可用。因此,我们需要降低到较低级别并创建一个实现装饰器模式的自定义RouteBase类来包装现有的Route类配置。

    此类只是在生成URL之前检查请求中的命名空间是否与特定命名空间匹配。

    public class NamespaceConstrainedRoute : RouteBase
    {
        private readonly string namespaceToMatch;
        private readonly RouteBase innerRoute;
    
        public NamespaceConstrainedRoute(string namespaceToMatch, RouteBase innerRoute)
        {
            if (string.IsNullOrEmpty(namespaceToMatch))
                throw new ArgumentNullException("namespaceToMatch");
            if (innerRoute == null)
                throw new ArgumentNullException("innerRoute");
            this.namespaceToMatch = namespaceToMatch;
            this.innerRoute = innerRoute;
        }
    
        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            return innerRoute.GetRouteData(httpContext);
        }
    
        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            object namespaces;
            if (requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out namespaces)
                && namespaces is IList<string>
                && ((IList<string>)namespaces).Contains(namespaceToMatch))
            {
                return innerRoute.GetVirtualPath(requestContext, values);
            }
    
            // null indicates to try to match the next route in the route table
            return null;
        }
    }
    

    用法

    var route1 = new Route(
            url: "CRUD/{controller}/{action}/{id}",
            defaults: new RouteValueDictionary(new { action = "Index", id = UrlParameter.Optional }),
            routeHandler: new MvcRouteHandler()
        )
    {
        DataTokens = new RouteValueDictionary
        {
            { "Namespaces",  new string[] { "College.Controllers.CRUD" }},
            { "UseNamespaceFallback", false }
        }
    };
    
    var route2 = new Route(
            url: "{controller}/{action}/{id}",
            defaults: new RouteValueDictionary(new { controller = "Home", action = "Index", id = UrlParameter.Optional }),
            routeHandler: new MvcRouteHandler()
        )
    {
        DataTokens = new RouteValueDictionary
        {
            { "Namespaces",  new string[] { "College.Controllers" }},
            { "UseNamespaceFallback", false }
        }
    };
    
    routes.Add(
        name: "CRUD",
        item: new NamespaceConstrainedRoute(
            namespaceToMatch: "College.Controllers.CRUD", 
            innerRoute: route1));
    
    routes.Add(
        name: "Default",
        item: new NamespaceConstrainedRoute(
            namespaceToMatch: "College.Controllers",
            innerRoute: route2));
    

    从这一点开始,如果您选择使上述配置看起来更清晰,则可以构建自己的MapRoute扩展方法。