这里的问题是您的 2 条路线在构建 URL 时不明确。基本上有3种方法可以解决这个问题:
- 添加另一个路由值以匹配不属于 URL 的部分。
- 使用
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 决定不在 IRouteConstraint 接口中提供 RequestContext 对象。这意味着有关请求绑定到哪个控制器的命名空间信息不可用。因此,我们需要降到较低的级别,并创建一个自定义的 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 扩展方法。