创建测试工程
分别在vs2013和vs2015中创建mvc项目,并创建First、Second、Three三个Area,每个Area下面创建一个HomeController和Index视图。修改RouteConfig.cs中的路由注册方法,添加命名空间
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, namespaces: new[] { "RouteDebuggerMvc5Demo.Controllers" } ); }
修改三个Area的路由注册方法,添加命名空间:
public class FirstAreaRegistration : AreaRegistration { public override string AreaName { get { return "First"; } } public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "First_default", "First/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional }, namespaces: new[] { "RouteDebuggerMvc5Demo.Areas.First.Controllers" } ); } }
使用nuget添加RouteDebugger引用,在Web.config中配置启用 <add key="RouteDebugger:Enabled" value="true" />,运行起来:
VS2013中升序
Matches Current Request | Url | Defaults | Constraints | DataTokens |
---|---|---|---|---|
False | First/{controller}/{action}/{id} | action = Index, id = UrlParameter.Optional | (empty) | Namespaces = RouteDebuggerMvc5Demo.Areas.First.Controllers, area = First, UseNamespaceFallback = False |
False | Second/{controller}/{action}/{id} | action = Index, id = UrlParameter.Optional | (empty) | Namespaces = RouteDebuggerMvc5Demo.Areas.Second.Controllers, area = Second, UseNamespaceFallback = False |
False | Three/{controller}/{action}/{id} | action = Index, id = UrlParameter.Optional | (empty) | Namespaces = RouteDebuggerMvc5Demo.Areas.Three.Controllers, area = Three, UseNamespaceFallback = False |
False | {resource}.axd/{*pathInfo} | (null) | (empty) | (null) |
True | {controller}/{action}/{id} | controller = Home, action = Index, id = UrlParameter.Optional | (empty) | Namespaces = RouteDebuggerMvc5Demo.Controllers |
True | {*catchall} | (null) | (null) | (null) |
VS2015中降序
Matches Current Request | Url | Defaults | Constraints | DataTokens |
---|---|---|---|---|
False | Three/{controller}/{action}/{id} | action = Index, id = UrlParameter.Optional | (empty) | Namespaces = RouteDebuggerDemo.Areas.Three.Controllers, RouteDebuggerDemo, area = Three, UseNamespaceFallback = False |
False | Second/{controller}/{action}/{id} | action = Index, id = UrlParameter.Optional | (empty) | Namespaces = RouteDebuggerDemo.Areas.Second.Controllers, RouteDebuggerDemo, area = Second, UseNamespaceFallback = False |
False | First/{controller}/{action}/{id} | action = Index, id = UrlParameter.Optional | (empty) | Namespaces = RouteDebuggerDemo.Areas.First.Controllers, RouteDebuggerDemo, area = First, UseNamespaceFallback = False |
False | {resource}.axd/{*pathInfo} | (null) | (empty) | (null) |
True | {controller}/{action}/{id} | controller = Home, action = Index, id = UrlParameter.Optional | (empty) | Namespaces = RouteDebuggerDemo.Controllers, RouteDebuggerDemo |
True | {*catchall} | (null) | (null) | (null) |
如果在VS2013中的某个路由注册有问题,一直没有显现出来,升级到VS2015后出现了404就有可能是路由匹配顺序导致的。问题参考:Why did the order of Areas in RegisterAllAreas change with Visual Studio 2015?
手动修改路由顺序
默认在 *.csproj文件中的路由顺序是
<Compile Include="Areas\First\Controllers\HomeController.cs" />
<Compile Include="Areas\First\FirstAreaRegistration.cs" />
<Compile Include="Areas\Second\Controllers\HomeController.cs" />
<Compile Include="Areas\Second\SecondAreaRegistration.cs" />
<Compile Include="Areas\Three\Controllers\HomeController.cs" />
<Compile Include="Areas\Three\ThreeAreaRegistration.cs" />
修改成
<Compile Include="Areas\First\Controllers\HomeController.cs" />
<Compile Include="Areas\First\FirstAreaRegistration.cs" />
<Compile Include="Areas\Three\Controllers\HomeController.cs" />
<Compile Include="Areas\Three\ThreeAreaRegistration.cs" />
<Compile Include="Areas\Second\Controllers\HomeController.cs" />
<Compile Include="Areas\Second\SecondAreaRegistration.cs" />
重新访问页面查看路由,顺序已经改变了(对比上面VS2015里的顺序),可以通过这种方法把最常用的路由调到最前面,提高匹配速度。经测试添加新的Area后,调整的路由顺序不会变回去,可以放心使用。
参考链接:.Net MVC Area Registration Sequence
RouteDebugger 简介
作者地址:ASP.NET Routing Debugger,代码结构图如下:
程序集属性[assembly: PreApplicationStartMethod(typeof(PreApplicationStart), "Start")] 使PreApplicationStart的Start方法在Application_Start方法之前执行,
public static void Start() { bool flag = Convert.ToBoolean(ConfigurationManager.AppSettings["RouteDebugger:Enabled"]); if (flag) { DynamicModuleUtility.RegisterModule(typeof(RouteDebuggerHttpModule)); } }
最后处理请求的是DebugHttpHandler里的ProcessRequest方法把匹配的路由信息获取出来,代码如下:
//处理请求 public void ProcessRequest(HttpContext context) { HttpRequest request = context.Request; if (!this.IsRoutedRequest(request) || context.Response.ContentType == null || !context.Response.ContentType.Equals("text/html", StringComparison.OrdinalIgnoreCase)) { return; } string text = string.Empty; RequestContext requestContext = request.RequestContext; if (request.QueryString.Count > 0) { RouteValueDictionary routeValueDictionary = new RouteValueDictionary(); foreach (string text2 in request.QueryString.Keys) { if (text2 != null) { routeValueDictionary.Add(text2, request.QueryString[text2]); } } VirtualPathData virtualPath = RouteTable.Routes.GetVirtualPath(requestContext, routeValueDictionary); if (virtualPath != null) { text = "<p><label style=\"font-weight: bold; font-size: 1.1em;\">Generated URL</label>: "; text = text + "<strong style=\"color: #00a;\">" + virtualPath.VirtualPath + "</strong>"; Route route = virtualPath.Route as Route; if (route != null) { text = text + " using the route \"" + route.Url + "\"</p>"; } } } string text3 = string.Empty; RouteData routeData = requestContext.RouteData; RouteValueDictionary values = routeData.Values; RouteBase route2 = routeData.Route; string text4 = string.Empty; using (RouteTable.Routes.GetReadLock()) { foreach (RouteBase current in RouteTable.Routes) { //查询请求与路由是否匹配 bool flag = current.GetRouteData(requestContext.HttpContext) != null; string isMatchRequest = string.Format("<span{0}>{1}</span>", DebugHttpHandler.BoolStyle(flag), flag); string url = "n/a"; string defaults = "n/a"; string constraints = "n/a"; string dataTokens = "n/a"; Route route3 = this.CastRoute(current); if (route3 != null) { url = route3.Url; defaults = DebugHttpHandler.FormatDictionary(route3.Defaults); constraints = DebugHttpHandler.FormatDictionary(route3.Constraints); dataTokens = DebugHttpHandler.FormatDictionary(route3.DataTokens); } text4 += string.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>", new object[] { isMatchRequest, url, defaults, constraints, dataTokens }); } } string text10 = "n/a"; string text11 = ""; if (!(route2 is DebugRoute)) { foreach (string current2 in values.Keys) { text3 += string.Format("\t<tr><td>{0}</td><td>{1} </td></tr>", current2, values[current2]); } foreach (string current3 in routeData.DataTokens.Keys) { text11 += string.Format("\t<tr><td>{0}</td><td>{1} </td></tr>", current3, routeData.DataTokens[current3]); } Route route4 = route2 as Route; if (route4 != null) { text10 = route4.Url; } } else { text10 = string.Format("<strong{0}>NO MATCH!</strong>", DebugHttpHandler.BoolStyle(false)); } text3 = "text3"; text10 = "text10"; text4 = "text4"; text11 = "text11"; text = "text";