以前我们使用RouteMonitor.dll进行MVC路由检测URL路径的映射匹配情况。由于公司电脑没有此组件,所以上网搜了下,结果才发现RouteMonitor.dll已经将名称改为了RouteDebug.dll 。
具体参阅 官方网站。
下载地址:http://files.cnblogs.com/Capricornus/RouteDebug-Binary.zip
使用方法:
1. 在MVC项目中添加引用此组件
2. 在全局应用程序类Global.asax.cs中设置代码
protected
void
Application_Start()
{
RegisterRoutes(RouteTable.Routes);
RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
//
以前RouteMonitor方式
//
RouteMonitor.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}
3.匹配路由如下图:
我们可以使用Reflector反编译这个RouteDebugger.dll组件,查看一下原理。如图:
RouteDebug中包含了DebugHttpHandler、DebugRoute、DebugRouteHandler、RouteDebugger这4个类。
首先从我们调用RouteDebug.RouteDebugger.RewriteRoutesForTesting的着手。
RouteDebugger类:
public
static
class
RouteDebugger
{
//
Methods
public
static
void
RewriteRoutesForTesting(RouteCollection routes)
{
using
(routes.GetReadLock())
{
bool
flag
=
false
;
foreach
(RouteBase base2
in
routes)
{
Route route
=
base2
as
Route;
if
(route
!=
null
)
{
route.RouteHandler
=
new
DebugRouteHandler();
}
if
(route
==
DebugRoute.Singleton)
{
flag
=
true
;
}
}
if
(
!
flag)
{
routes.Add(DebugRoute.Singleton);
}
}
}
}
首先,整个代码是使用System.Web.Routing命名空间下的RouteCollection.GetReadLock()锁定的,提供一个对象,用于管理在从集合中检索对象时的线程安全性;然后遍历我们传过来的路由集合参数。用RouteDebug中的DebugRouteHandler去替换原有RouteHandler,以便改变Http处理程序的方向,接着将Singletion属性的值添加到路由结合中。
DebugRoute类:
public
class
DebugRoute : Route
{
private
static
DebugRoute singleton
=
new
DebugRoute();
private
DebugRoute() :
base
(
"
{*catchall}
"
,
new
DebugRouteHandler())
{
}
public
static
DebugRoute Singleton
{
get
{
return
singleton;
}
}
}
DebugRoute继承与Route类,构造函数实现了构造可捕获所有URL地址的Route。
DebugRouteHandler路由处理程序类:
public
class
DebugRouteHandler : IRouteHandler
{
//
Methods
public
IHttpHandler GetHttpHandler(RequestContext requestContext)
{
DebugHttpHandler handler
=
new
DebugHttpHandler();
handler.RequestContext
=
requestContext;
return
handler;
}
}
实现IHttpHanlder接口的实例化对象,传入了一个RequestContext对象实例。
DebugHttpHandler类:
public
class
DebugHttpHandler : IHttpHandler
{
[CompilerGenerated]
private
RequestContext
<
RequestContext
>
k__BackingField;
private
static
string
FormatRouteValueDictionary(RouteValueDictionary values)
{
if
((values
==
null
)
||
(values.Count
==
0
))
{
return
"
(null)
"
;
}
string
str
=
string
.Empty;
foreach
(
string
str2
in
values.Keys)
{
str
=
str
+
string
.Format(
"
{0} = {1},
"
, str2, values[str2]);
}
if
(str.EndsWith(
"
,
"
))
{
str
=
str.Substring(
0
, str.Length
-
2
);
}
return
str;
}
public
void
ProcessRequest(HttpContext context)
{
string
str
=
string
.Empty;
if
(context.Request.QueryString.Count
>
0
)
{
RouteValueDictionary dictionary
=
new
RouteValueDictionary();
foreach
(
string
str2
in
context.Request.QueryString.Keys)
{
dictionary.Add(str2, context.Request.QueryString[str2]);
}
VirtualPathData virtualPath
=
RouteTable.Routes.GetVirtualPath
(
this
.RequestContext, dictionary);
if
(virtualPath
!=
null
)
{
str
=
"
<p><label>Generated URL</label>:
"
;
str
=
str
+
"
<strong style=\
"
color: #00a;\
"
>
"
+
virtualPath.VirtualPath
+
"
</strong>
"
;
Route route
=
virtualPath.Route
as
Route;
if
(route
!=
null
)
{
str
=
str
+
"
using the route \
""
+ route.Url +
"
\
"
</p>
"
;
}
}
}
string
format
=
"
<html>\r\n<head>\r\n
<title>Route Tester</title>\r\n <style>\r\n
body, td, th {{font-family: verdana; font-size: small;}}\r\n
.message {{font-size: .9em;}}\r\n
caption {{font-weight: bold;}}\r\n
tr.header {{background-color: #ffc;}}\r\n
label {{font-weight: bold; font-size: 1.1em;}}\r\n
.false {{color: #c00;}}\r\n
.true {{color: #0c0;}}\r\n
</style>\r\n</head>\r\n<body>\r\n<h1>Route Tester</h1>\r\n<div id=\
"
main\
"
>\r\n
<p class=\
"
message\
"
>\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 class=\
"
message\
"
>\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>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>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 <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
</body>\r\n</html>
"
;
string
str4
=
string
.Empty;
//
RouteData类包含所请求路由的相关值
RouteData routeData
=
this
.RequestContext.RouteData;
//
获得路由的URL参数值和默认值的集合
RouteValueDictionary values
=
routeData.Values;
//
获取路由的对象
RouteBase base2
=
routeData.Route;
string
str5
=
string
.Empty;
using
(RouteTable.Routes.GetReadLock())
{
foreach
(RouteBase base3
in
RouteTable.Routes)
{
//
返回有关集合中与指定值匹配的路由的信息,如果为空,说明不匹配
bool
flag
=
base3.GetRouteData(
this
.RequestContext.HttpContext)
!=
null
;
string
str6
=
string
.Format(
"
<span class=\
"
{
0
}\
"
>{0}</span>
"
, flag);
string
url
=
"
n/a
"
;
string
str8
=
"
n/a
"
;
string
str9
=
"
n/a
"
;
string
str10
=
"
n/a
"
;
Route route2
=
base3
as
Route;
if
(route2
!=
null
)
{
//
如果路由不为空,得到匹配的Url路由
url
=
route2.Url;
//
得到默认的Url匹配规则信息
str8
=
FormatRouteValueDictionary(route2.Defaults);
//
得到约束的Url匹配规则信息
str9
=
FormatRouteValueDictionary(route2.Constraints);
//
得到命名空间的Url匹配规则信息
str10
=
FormatRouteValueDictionary(route2.DataTokens);
}
str5
=
str5
+
string
.Format
(
"
<tr><td>{0}</td><td>{1}</td><td>{2}</td>
<td>{3}</td><td>{4}</td></tr>
"
,
new
object
[] { str6, url, str8, str9, str10 });
}
}
string
str11
=
"
n/a
"
;
string
str12
=
""
;
//
如果只被{@cacheall}捕获时,提示不匹配
if
(base2
is
DebugRoute)
{
str11
=
"
<strong class=\
"
false
\
"
>NO MATCH!</strong>
"
;
}
else
{
//
匹配的路由信息
foreach
(
string
str2
in
values.Keys)
{
str4
=
str4
+
string
.Format
(
"
\t<tr><td>{0}</td><td>{1} </td></tr>
"
, str2, values[str2]);
}
foreach
(
string
str2
in
routeData.DataTokens.Keys)
{
str12
=
str12
+
string
.Format(
"
\t<tr><td>{0}</td><td>{1} </td></tr>
"
,
str2, routeData.DataTokens[str2]);
}
Route route3
=
base2
as
Route;
if
(route3
!=
null
)
{
str11
=
route3.Url;
}
}
context.Response.Write(
string
.Format(format,
new
object
[]
{ str4, str11, str5, context.Request.AppRelativeCurrentExecutionFilePath, str12, str }));
}
public
bool
IsReusable
{
get
{
return
true
;
}
}
public
RequestContext RequestContext
{
[CompilerGenerated]
get
{
return
this
.
<
RequestContext
>
k__BackingField;
}
[CompilerGenerated]
set
{
this
.
<
RequestContext
>
k__BackingField
=
value;
}
}
}
通过ProcessRequest方法来处理请求,最后呈现在路由检测的页面上。
首先从RequestContext.RouteData可以得到RouteData类,RouteData类包含所请求路由的相关值。从RouteData.Values获取路由的URL参数值和默认值集合,在从RouteData.Route获取路由的对象,在获取有关集合中与指定值匹配的路由信息.