今天开始学习ASP.NET MVC,在看《ASP.NET MVC架构与实战》时,看到有这样一个组件 RouteMonitor.dll,以为挺实用的,能够用来检测Url路径的映射匹配状况,只要在浏览器中输入请求地址,就能够获得匹配的状况,而且以一种友好的页面展示给咱们,以下图所示:
html
图一
因而乎,决定先本身分析一下该原理。浏览器
1. 咱们都知道一个应用程序启动是从Application_Start事件开始的,在建立一个新的ASP.NET MVC应用程序的时候,默认会在该事件中添加多线程
RegisterRoutes(RouteTable.Routes);架构
接着RegisterRoutes方法里面编写一些路由映射的方法,将请求的URL映射到相应的控制器中。
2. 如今将Application_Start事件中改写成这样的代码:
函数
1
protected
void
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类都作了些什么:
1
public
static
class
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()可对路由集合类进行多线程同步访问:
学习
1
public
IDisposable GetReadLock()
2
{
3
this
._rwLock.AcquireReaderLock(
-
1
);
4
return
new
ReadLockDisposable(
this
._rwLock);
5
}
AcquireReaderLock方法去请求获得该读写锁,而且设置超时时间为Infinite(无限),当using方法域结束后将释放该读写锁。ui
(另:ReaderWriterLock 用于同步对资源的访问。在任一特定时刻,它容许多个线程同时进行读访问,或者容许单个线程进行写访问。在资源不常常发生更改的状况下,ReaderWriterLock 所提供的吞吐量比简单的一次只容许一个线程的锁(如 Monitor)更高。)this
接着从routes遍历全部的Route类,在这里咱们改变它的路由处理程序,该路由处理程序类的接口为IRouteHandler,用RouteMonitor自带的DebugRouteHandler去替换它原有的RouteHandler,以便后面改变Http处理程序的“方向”。url
咱们先接着看后面的代码,这里有个routes.Add(DebugRoute.Singleton),DubugRoute继承于Route类,它的构造函数实现于构造可捕获全部URL地址的Route。DebugRoute.Singleton做为单一实例,代码以下:
spa
1
public
class
DebugRoute : Route
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的路由处理程序:
1
public
class
DebugRouteHandler : IRouteHandler
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处理的程序:
1
public
void
ProcessRequest(HttpContext context)
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
}
经过它ProcessRequest来处理请求,最后呈现路由检测的页面。
从中能够看到,首先从RequestContext.RouteData能够获得RouteData类,RouteData类包含所请求路由的相关值。
从RouteData.Values获取路由的 URL 参数值和默认值的集合。
从routeData.Route获取路由的对象。
GetRouteData的方法获取有关集合中与指定值匹配的路由的信息。
经过调用FormatRouteValueDictionary方法获得每一条路由的相关值:
1
private
static
string
FormatRouteValueDictionary(RouteValueDictionary values)
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 }));页面中就呈现出路由匹配的检测信息了。