今天开始学习ASP.NET MVC,在看《ASP.NET MVC架构与实战》时,看到有这样一个组件 RouteMonitor.dll,觉得挺实用的,可以用来检测Url路径的映射匹配情况,只要在浏览器中输入请求地址,就可以得到匹配的情况,并且以一种友好的页面展现给我们,如下图所示:
图一
于是乎,决定先自己分析一下该原理。
1. 我们都知道一个应用程序启动是从Application_Start事件开始的,在创建一个新的ASP.NET MVC应用程序的时候,默认会在该事件中添加
RegisterRoutes(RouteTable.Routes);
接着RegisterRoutes方法里面编写一些路由映射的方法,将请求的URL映射到相应的控制器中。
2. 现在将Application_Start事件中改写成这样的代码:
2 {
3 RegisterRoutes(RouteTable.Routes);
4 RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
5 }
注意到第3行的代码,引用的就是RouteMonitor.dll的组件,通过RouteDebugger的静态方法RewriteRoutesForTesting,并且赋予一个RouteCollection的路由集合的参数,将页面映射到图一的页面,便于查看路由映射的情况。
3. 现在开始分析下RouteMonitor.dll里面都有些什么呢。
里面包含DebugHttpHandler类,DebugRoute类,DebugRouteHandler类,RouteDebugger类,我们先看看RouteDebugger类都做了些什么:
2 {
3 public static void RewriteRoutesForTesting(RouteCollection routes)
4 {
5 // 可对路由集合类进行多线程同步访问
6 using (routes.GetReadLock())
7 {
8 bool flag = false ;
9 foreach (RouteBase base2 in routes)
10 {
11 Route route = base2 as Route;
12 if (route != null )
13 {
14 route.RouteHandler = new DebugRouteHandler();
15 }
16 if (route == DebugRoute.Singleton)
17 {
18 flag = true ;
19 }
20 }
21 if ( ! flag)
22 {
23 routes.Add(DebugRoute.Singleton);
24 }
25 }
26 }
27 }
其中routes.GetReadLock()可对路由集合类进行多线程同步访问:
2 {
3 this ._rwLock.AcquireReaderLock( - 1 );
4 return new ReadLockDisposable( this ._rwLock);
5 }
AcquireReaderLock方法去请求得到该读写锁,并且设置超时时间为Infinite(无限),当using方法域结束后将释放该读写锁。
(另:ReaderWriterLock 用于同步对资源的访问。在任一特定时刻,它允许多个线程同时进行读访问,或者允许单个线程进行写访问。在资源不经常发生更改的情况下,ReaderWriterLock 所提供的吞吐量比简单的一次只允许一个线程的锁(如 Monitor)更高。)
接着从routes遍历所有的Route类,在这里我们改变它的路由处理程序,该路由处理程序类的接口为IRouteHandler,用RouteMonitor自带的DebugRouteHandler去替换它原有的RouteHandler,以便后面改变Http处理程序的“方向”。
我们先接着看后面的代码,这里有个routes.Add(DebugRoute.Singleton),DubugRoute继承于Route类,它的构造函数实现于构造可捕获所有URL地址的Route。DebugRoute.Singleton作为单一实例,代码如下:
2 {
3 private static DebugRoute singleton = new DebugRoute();
4
5 // 可捕获所有的URL地址的Route
6 private DebugRoute()
7 : base ( " {*catchall} " , new DebugRouteHandler())
8 {
9 }
10
11 public static DebugRoute Singleton
12 {
13 get
14 {
15 return singleton;
16 }
17 }
18 }
4. 接着分析DebugRouteHandler的路由处理程序:
2 {
3 public IHttpHandler GetHttpHandler(RequestContext requestContext)
4 {
5 DebugHttpHandler handler = new DebugHttpHandler();
6 handler.RequestContext = requestContext;
7 return handler;
8 }
9 }
这样它可以获得实现IHttpHanlder接口的DebugHttpHandler类的实例化对象,这个实例化对象中也传入了一个RequestContext 对象实例。
5. 最后看下这个以IHttpHanlder为接口的DebugHttpHandler类,用于进行Http处理的程序:
2 {
3 string format = " <html>\r\n<head>\r\n <title>路由监测</title>\r\n <style>\r\n body, td, th {{font-family: verdana; font-size: .8em;}}\r\n caption {{font-weight: bold;}}\r\n tr.header {{background-color: #ffc;}}\r\n label {{font-weight: bold; }}\r\n .false {{color: #c00;}}\r\n .true {{color: #0c0;}}\r\n </style>\r\n</head>\r\n<body>\r\n<div id=\ " main\ " >\r\n <p class=\ " message\ " >\r\n 在浏览器中键入请求地址,可以监测匹配的路由。\r\n </p>\r\n <p><label>Route</label>: {1}</p>\r\n <div style=\ " float : left;\ " >\r\n <table border=\ " 1 \ " cellpadding=\ " 3 \ " cellspacing=\ " 0 \ " width=\ " 300 \ " >\r\n <caption>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>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>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 <strong>AppRelativeCurrentExecutionFilePath</strong>: {3}\r\n</div>\r\n</body>\r\n</html> " ;
4 string str2 = string .Empty;
5
6 // RouteData类包含所请求路由的相关值
7 RouteData routeData = this .RequestContext.RouteData;
8
9 // 获得路由的URL参数值和默认值的集合
10 RouteValueDictionary values = routeData.Values;
11
12 // 获取路由的对象
13 RouteBase base2 = routeData.Route;
14
15 string str3 = string .Empty;
16 using (RouteTable.Routes.GetReadLock())
17 {
18 foreach (RouteBase base3 in RouteTable.Routes)
19 {
20 // 返回有关集合中与指定值匹配的路由的信息,如果为空,说明不匹配
21 bool flag = base3.GetRouteData( this .RequestContext.HttpContext) != null ;
22 string str4 = string .Format( " <span class=\ " { 0 }\ " >{0}</span> " , flag);
23 string url = " n/a " ;
24 string str6 = " n/a " ;
25 string str7 = " n/a " ;
26 string str8 = " n/a " ;
27 Route route = base3 as Route;
28
29 // 如果路由不为空
30 if (route != null )
31 {
32 // 得到匹配的Url路由
33 url = route.Url;
34
35 // 得到默认的Url匹配规则信息
36 str6 = FormatRouteValueDictionary(route.Defaults);
37 // 得到约束的Url匹配规则信息
38 str7 = FormatRouteValueDictionary(route.Constraints);
39 // 得到命名空间的Url匹配规则信息
40 str8 = FormatRouteValueDictionary(route.DataTokens);
41 }
42 str3 = str3 + string .Format( " <tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{3}</td></tr> " , new object [] { str4, url, str6, str7, str8 });
43 }
44 }
45 string str9 = " n/a " ;
46 string str10 = "" ;
47
48 // 如果只被{@cacheall}捕获时,提示不匹配
49 if (base2 is DebugRoute)
50 {
51 str9 = " <strong class=\ " false \ " >NO MATCH!</strong> " ;
52 }
53 else
54 {
55 // 匹配的路由信息
56 foreach ( string str11 in values.get_Keys())
57 {
58 str2 = str2 + string .Format( " \t<tr><td>{0}</td><td>{1} </td></tr> " , str11, values.get_Item(str11));
59 }
60 foreach ( string str11 in routeData.DataTokens.get_Keys())
61 {
62 str10 = str10 + string .Format( " \t<tr><td>{0}</td><td>{1} </td></tr> " , str11, routeData.DataTokens.get_Item(str11));
63 }
64 Route route2 = base2 as Route;
65 if (route2 != null )
66 {
67 str9 = route2.Url;
68 }
69 }
70 context.Response.Write( string .Format(format, new object [] { str2, str9, str3, context.Request.AppRelativeCurrentExecutionFilePath, str10 }));
71 }
通过它Proce***equest来处理请求,最后呈现路由检测的页面。
从中可以看到,首先从RequestContext.RouteData可以得到RouteData类,RouteData类包含所请求路由的相关值。
从RouteData.Values获取路由的 URL 参数值和默认值的集合。
从routeData.Route获取路由的对象。
GetRouteData的方法获取有关集合中与指定值匹配的路由的信息。
通过调用FormatRouteValueDictionary方法得到每一条路由的相关值:
2 {
3 if (values == null )
4 {
5 return " (null) " ;
6 }
7 string str = string .Empty;
8 // 遍历路由键/值对的集合
9 foreach ( string str2 in values.get_Keys())
10 {
11 str = str + string .Format( " {0} = {1}, " , str2, values.get_Item(str2));
12 }
13 if (str.EndsWith( " , " ))
14 {
15 str = str.Substring( 0 , str.Length - 2 );
16 }
17 return str;
18 }
通过重写
context.Response.Write(string.Format(format, new object[] { str2, str9, str3, context.Request.AppRelativeCurrentExecutionFilePath, str10 }));
页面中就呈现出路由匹配的检测信息了。
转载于:https://blog.51cto.com/kebin/504321