ASP.NET MVC约定路由与特性路由共存异常:启用后约定路由报404
这个问题我之前也碰到过!当ASP.NET MVC里同时启用特性路由和约定路由时,出现404大多是因为特性路由的规则“接管”了原本属于约定路由的请求匹配,或者路由特性的使用方式让约定路由无法命中。咱们一步步来排查解决:
1. 控制器/Action被路由特性“独占”了
如果你的控制器上标记了[RoutePrefix],或者某个Action单独加了[Route]特性,默认情况下这个控制器的约定路由(比如/Home/Index)就会失效——因为特性路由的匹配优先级更高,而且一旦控制器/Action绑定了特性路由,ASP.NET MVC就会默认不再用约定路由来匹配它了。
举个例子,如果你的Home控制器是这样的:
[RoutePrefix("home")] public class HomeController : Controller { public ActionResult Index() { ... } }
那原来的/Home/Index就会404,必须用/home/Index访问。
解决办法:
如果想让控制器同时兼容特性路由和约定路由,只需要在控制器上额外加一个兼容约定路由的[Route]特性:
[RoutePrefix("home")] [Route("{action=Index}")] // 这个模板允许通过约定路由的方式访问Action public class HomeController : Controller { // 既可以通过/home/Index访问,也能通过/Home/Index访问 public ActionResult Index() { ... } // 可以通过/home/custom-about访问,也能通过/Home/About访问 [Route("custom-about")] public ActionResult About() { ... } }
如果只是个别Action加了[Route],其他Action想要保留约定路由,那只要不对那些Action加路由特性就行——没加特性的Action依然可以通过约定路由访问。
2. 路由注册顺序(你的代码其实是对的,但还是提一下)
路由匹配是按注册顺序来的,先注册的路由先尝试匹配。你的代码里先调用routes.MapMvcAttributeRoutes()注册特性路由,再注册默认约定路由,这个顺序是正确的——如果反过来,约定路由会先拦截请求,导致特性路由无法生效,但你现在的问题是约定路由404,所以这个大概率不是原因,但还是确认下不要搞反顺序。
3. 特性路由模板和约定路由冲突
如果某个特性路由的模板和默认约定路由的{controller}/{action}/{id}完全重叠,比如你有个Action加了[Route("{controller}/{action}")],那所有符合约定路由规则的请求都会先被这个特性路由匹配,如果对应的Action不存在,就会返回404。
解决办法:
检查所有特性路由的模板,避免和约定路由的模板重复;如果必须用类似的模板,可以给特性路由添加约束,比如指定控制器名称:
[Route("Home/{action}")] public ActionResult SomeAction() { ... }
这样就只会匹配Home控制器的请求,不会影响其他控制器的约定路由。
你的路由注册代码是正确的,不需要修改,只要调整控制器上的路由特性即可:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); // 先注册特性路由,顺序正确 routes.MapMvcAttributeRoutes(); // 再注册默认约定路由 routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }
内容的提问来源于stack exchange,提问作者Adrian

