今天開始學習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 }));頁面中就呈現出路由匹配的檢測信息了。