ASP.NET MVC路由匹配檢測組件的代碼分析

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

相關文章
相關標籤/搜索