程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> ASP.NET MVC路由匹配檢測組件的代碼分析

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

編輯:.NET實例教程

今天開始學習ASP.NET MVC,在看《ASP.Net MVC架構與實戰》時,看到有這樣一個組件 RouteMonitor.dll,覺得挺實用的,可以用來檢測Url路徑的映射匹配情況,只要在浏覽器中輸入請求地址,就可以得到匹配的情況,並且以一種友好的頁面展現給我們,如下圖所示:

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

  圖一

  於是乎,決定先自己分析一下該原理。

  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裡面都有些什麼呢。

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

 裡面包含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方法域結束後將釋放該讀寫鎖。

  (另:ReaderWriterLock 用於同步對資源的訪問。在任一特定時刻,它允許多個線程同時進行讀訪問,或者允許單個線程進行寫訪問。在資源不經常發生更改的情況下,ReaderWriterLock 所提供的吞吐量比簡單的一次只允許一個線程的鎖(如 Monitor)更高。)

  接著從routes遍歷所有的Route類,在這裡我們改變它的路由處理程序,該路由處理程序類的接口為IRouteHandler,用 RouteMonitor自帶的DebugRouteHandler去替換它原有的RouteHandler,以便後面改變Http處理程序的“方向”。

  我們先接著看後面的代碼,這裡有個routes.Add(DebugRoute.Singleton),DubugRoute繼承於Route類,它的構造函數實現於構造可捕獲所有URL地址的Route。DebugRoute.Singleton作為單一實例,代碼如下:

 1 public class DebugRoute : Route
 2 {
 3     private static DebugRoute singleton = new DebugRoute(); 
 4 
 5     //可捕獲所有的URL地址的Rou
te
 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.R
equestContext.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獲取路由的對象。

  GetRo
uteData的方法獲取有關集合中與指定值匹配的路由的信息。

  通過調用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 }));

  頁面中就呈現出路由匹配的檢測信息了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved