我已经设置了以下路由,以便可以使用重复的控制器名称(在不同的命名空间中)。这工作正常,但是当我从任何控制器使用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中指定路由来解决这个问题,但我不想这样做,因为我想在将来重新构建并且我的更改将被覆盖。
答案 0 :(得分:0)
这里的问题是,在构建URL时,您的2条路线不明确。基本上有3种方法可以解决这个问题:
RouteLink
按名称指定路线(以及其他路线值条件以使其匹配)。由于您明确声明第二个选项是不可接受的,因此以下是第一个选项的示例:
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
扩展方法。