VS2013和VS2015中MVC 区域路由匹配顺序相反

 

创建测试工程

分别在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中降序

All Routes
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}&nbsp;</td></tr>", current2, values[current2]);
}
foreach (string current3 in routeData.DataTokens.Keys)
{
text11 += string.Format("\t<tr><td>{0}</td><td>{1}&nbsp;</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"; context.Response.Write(string.Format("<html>\r\n<div id=\"haackroutedebugger\" style=\" padding-bottom: 10px;\">\r\n <style>\r\n #haackroutedebugger, #haackroutedebugger td, #haackroutedebugger th {{background-color: #fff; font-family: verdana, helvetica, san-serif; font-size: small;}}\r\n #haackroutedebugger tr.header td, #haackroutedebugger tr.header th {{background-color: #ffc;}}\r\n </style>\r\n <hr style=\"width: 100%; border: solid 1px #000; margin:0; padding:0;\" />\r\n <h1 style=\"margin: 0; padding: 4px; border-bottom: solid 1px #bbb; padding-left: 10px; font-size: 1.2em; background-color: #ffc;\">Route Debugger</h1>\r\n <div id=\"main\" style=\"margin-top:0; padding: 0 10px;\">\r\n <p style=\"font-size: .9em; padding-top:0\">\r\n Type in a url in the address bar to see which defined routes match it. \r\n A {{*catchall}} route is added to the list of routes automatically in \r\n case none of your routes match.\r\n </p>\r\n <p style=\"font-size: .9em;\">\r\n To generate URLs using routing, supply route values via the query string. example: <code>http://localhost:14230/?id=123</code>\r\n </p>\r\n <p><label style=\"font-weight: bold; font-size: 1.1em;\">Matched Route</label>: {1}</p>\r\n {5}\r\n <div style=\"float: left;\">\r\n <table border=\"1\" cellpadding=\"3\" cellspacing=\"0\" width=\"300\">\r\n <caption style=\"font-weight: bold;\">Route Data</caption>\r\n <tr class=\"header\"><th>Key</th><th>Value</th></tr>\r\n {0}\r\n </table>\r\n </div>\r\n <div style=\"float: left; margin-left: 10px;\">\r\n <table border=\"1\" cellpadding=\"3\" cellspacing=\"0\" width=\"300\">\r\n <caption style=\"font-weight: bold;\">Data Tokens</caption>\r\n <tr class=\"header\"><th>Key</th><th>Value</th></tr>\r\n {4}\r\n </table>\r\n </div>\r\n <hr style=\"clear: both;\" />\r\n <table border=\"1\" cellpadding=\"3\" cellspacing=\"0\">\r\n <caption style=\"font-weight: bold;\">All Routes</caption>\r\n <tr class=\"header\">\r\n <th>Matches Current Request</th>\r\n <th>Url</th>\r\n <th>Defaults</th>\r\n <th>Constraints</th>\r\n <th>DataTokens</th>\r\n </tr>\r\n {2}\r\n </table>\r\n <hr />\r\n <h3>Current Request Info</h3>\r\n <p>\r\n AppRelativeCurrentExecutionFilePath is the portion of the request that Routing acts on.\r\n </p>\r\n <p><strong>AppRelativeCurrentExecutionFilePath</strong>: {3}</p>\r\n </div>\r\n</div>", new object[]
{
text3,
text10,
text4,
request.AppRelativeCurrentExecutionFilePath,
text11,
text
}));
}

注:RouteDebugger的源码是通过ILSpy反编译的。

结束语

抽出时间去验证路由顺序是因为两个错误引起的:

1.升级vs2013的mvc4解决方案到vs2015后所有的Area路由失效,返回404错误。以为是vs2015对mvc4的支持不够(刚出来是对mvc4是有点问题),就撤销升级返回vs2013了。

2.在vs2013中添加了一个新的Area,排在了以前的所有路由之后,出现404错误。通过RouteDebugger发现是路由的顺序问题,所以想到上面那个问题应该也是顺序问题,不应该是vs2015的缺陷。

MVC 区域路的更多相关文章

  1. MVC区域 视图必须派生自 WebViewPage 或 WebViewPage<TModel>

    http://blog.csdn.net/iack_ji/article/details/16965885 今天在学习 mvc区域时,将区域控制器类 外迁到其他的程序集的练习中出现了"视图必 ...

  2. 我的前端MVC之路

    大约十几个月前,了解到时下前端MVC之火爆,同事推荐我了解一下angular.当时也不是特别在意,只是稍稍阅读了一遍官方文档,并尝试了文档上的例子.其实当时也颇有震惊之感的,原来代码还可以这么写!看完 ...

  3. MVC区域小结

    MVC区域小结 MVC区域小结 MVC3一直在学习,项目中有的时候也会用到,博客园也一直逛,想写点什么东西,可惜我这个人平时都很懒,理论层面的东西自己写不来,还是来点实际的简单入门的博客,对自己总结能 ...

  4. 将子域名请求路由到MVC区域

    写了个扩展,分享给需要的朋友. 0x01 使用方法 在mvc区域中的{xxxx}AreaRegistration.cs文件中,如ProjectsAreaRegistration.cs <pre& ...

  5. mvc路由,mvc区域

    1.路由在进行匹配时,会默认匹配第一个路由信息 2.路由规则的{control}和{action}时不能改变的 3.路由规则可以中间字符可以随便定义,但是{control}和{action}必须使用一 ...

  6. MVC 区域功能

    因为MVC项目是要求都放在固定的文件夹,所以,当项目大的时候,会很不方便管理,所以微软引入的区域的功能 使用方法: 在项目上右击--添加--区域 就会出现Areas的文件夹,里面就是子MVC 渲染: ...

  7. MVC区域使用

    新建项目 Main: 添加一个MVC5控制器并添加index视图:(HomeController) Views/Home/Index.cshtml内容: @{ Layout = null; } < ...

  8. MVC 区域模块

    mvc4.0新增的area区域机制,可以协助你在架构较为大型的项目,让独立性较高的部分功能独立成一个MVC子网站,以降低网站与网站之间的耦合性,也可以通过area的切割,让多人同时开发同一个项目时候, ...

  9. MVC教程:MVC区域路由

    一.区域路由 为了管理网站中大量的文件,在ASP.NET MVC 2.0版本中引入了一个新概念:区域(Area). 有了区域以后,可以让我们的项目不至于太复杂而导致管理混乱.每个模块的页面都放入相应的 ...

随机推荐

  1. 【刷题】BZOJ 1823 [JSOI2010]满汉全席

    Description 满汉全席是中国最丰盛的宴客菜肴,有许多种不同的材料透过满族或是汉族的料理方式,呈现在數量繁多的菜色之中.由于菜色众多而繁杂,只有极少數博学多闻技艺高超的厨师能够做出满汉全席,而 ...

  2. 【CF912E】Prime Game(meet in the middle)

    [CF912E]Prime Game(meet in the middle) 题面 CF 懒得翻译了. 题解 一眼题. \(meet\ in\ the\ middle\)分别爆算所有可行的两组质数,然 ...

  3. 【bzoj4009】 HNOI2015—接水果

    http://www.lydsy.com/JudgeOnline/problem.php?id=4009 (题目链接) 题意 给出一颗无根树.有一些路径记为$P_i$,这些路径有两个端点和一个权值$W ...

  4. POJ 2240 Arbitrage / ZOJ 1092 Arbitrage / HDU 1217 Arbitrage / SPOJ Arbitrage(图论,环)

    POJ 2240 Arbitrage / ZOJ 1092 Arbitrage / HDU 1217 Arbitrage / SPOJ Arbitrage(图论,环) Description Arbi ...

  5. 【CF580C】Kefa and Park

    题目大意:给定一棵 N 个节点的有根树(其中根节点始终为 1 号节点),点有点权,点权只有 1 和 0 两种,求从根节点到叶子节点的路径中,有多少条路径满足:路径上最大连续点权为 1 的节点个数不超过 ...

  6. 监控IIS的运行状态

    IIS经常出现假死的情况,具体什么时候会出现假死,我就不说了,今天我要写的是如何监控IIS的状态. 程序的功能是:如果IIS是为运行的状态,就重启IIS,如果IIS的连接数达到了设置的连接数,也重启I ...

  7. C++原型模式和模板模式

    DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.其中有一个词很重要,那就是拷贝.可以说,拷贝是原型模式的精髓所在.举个现实中的例子来介绍原型模式.找工作的时候,我们需 ...

  8. Elasticsearch 5.0 安装 Search Guard 5 插件

    一.Search Guard 简介 Search Guard  是 Elasticsearch 的安全插件.它为后端系统(如LDAP或Kerberos)提供身份验证和授权,并向Elasticsearc ...

  9. OpenStack 存储服务 Cinder存储节点部署NFS(十七)

    Cinder存储节点部署 1.安装软件包 yum install -y nfs-utils rpcbind 提示:早期版本安装portmap nfs-utils :包括基本的NFS命令与监控程序 rp ...

  10. [整理]ASP.NET MVC 5

    1.入门 1.1官方资料 http://www.asp.net/mvc/overview/getting-started/introduction/getting-started 疑问: startu ...