上周我寫了三篇文章(一、二、三)詳細地介紹了ASP.NET的路由系統。ASP.NET的路由系統旨在通過注冊URL模板與物理文件之間的映射進而實現請求地址與文件路徑之間的分離,但是對於ASP.NET MVC應用來說,請求的目標不再是一個具體的物理文件,而是定義在某個Controller類型中的Action方法。出於自身路由特點的需要,ASP.NET對ASP.NET的路由系統進行了相應的擴展。
一、基本路由映射
通過前面的介紹我們知道基於某個物理文件的路由映射通過調用代表全局路由表的RouteTable的靜態屬性Routes(一個RouteCollection對象)的MapPageRoute方法來完成,為了實現針對目標Controller和Action的路由,ASP.NET MVC針對RouteCollection類型定義了一系列的擴展方法以實現文件路徑無關的路由映射,這些擴展方法定義在RouteCollectionExtensions類型中。如下面的代碼片斷所示,RouteCollectionExtensions定義了兩組方法,方法IgnoreRoute用於注冊不需要進行路由的URL模板,對應於RouteCollectionExtensions的Ignore方法;仿佛MapRoute用於進行基於URL模板的路由注冊,對應於RouteCollectionExtensions的MapPageRoute方法。
1: public static class RouteCollectionExtensions
2: {
3: //其他成員
4: public static void IgnoreRoute(this RouteCollection routes, string url);
5: public static void IgnoreRoute(this RouteCollection routes, string url, object constraints);
6:
7: public static Route MapRoute(this RouteCollection routes, string name, string url);
8: public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults);
9: public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces);
10: public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints);
11: public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces);
12: public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);
13: }
由於ASP.NET MVC的路由注冊與具體的物理文件無關,所以MapRoute方法中並沒有一個表示文件路徑的physicalFile參數。與直接定義在RouteCollectionExtensions中的Ignore和MapPageRoute方法不同的是,表示默認變量的參數defaults和基於正則表達式的變量約束的參數constraints都不再是一個RouteValueDictionary對象,而是一個普通的object。這主要是為了編程上的便利,使得我們可以通過匿名類型的方式來指定這兩個參數值。該方法在內部會通過反射的方式得到指定對象所有屬性值,並轉換為RouteValueDictionary對象,其屬性名和屬性值作為字典元素的Key和Value。
對於ASP.NET MVC來說,最終需要通過在請求地址中指定的Controller名稱來創建具體的Controller實例。由於Controller名稱 僅僅對應著類型的名稱,Controller的成功實例化的前提是我們能夠正確地解析出它的具體類型,所以我們需要使用了命名空間。在調用MapRoute方法的時候我們可以通過字符串數組類型的參數namespaces來指定一個命名空間的列表。對於注冊的命名空間,可以指定一個代表完整命名空間的字符串,也可以使用“*”作為通配符。
添加的命名控件列表最終是被存儲於Route對象的DataTokens屬性中,對應的Key為“Namespaces”。MapRoute方法沒有為初始化Route對象的DataTokens屬性提供相應的參數,如果沒有指定命名空間列表,所有通過該方法添加的Route對象的DataTokens屬性總是一個空的RouteValueDictionary對象。
對於針對定義在某個Controller中的某個Action的請求,如果注冊的路由表與之匹配,具體匹配的某個路由對象的GetRouteData被調用並返回一個具體的RouteData對象。根據對請求地址進行解析得到的目標Controller和Action的名稱必須包含在該RouteData的Values屬性對應的RouteValueDictionary對象中,其對應的Key分別為controller和action。
二、 實例演示:注冊路由映射與查看路由信息
ASP.NET MVC通過定義在RouteCollectionExtensions中的擴展方法MapRoute進行路由映射,為了讓讀者對此有一個深刻的認識,我們來進行一個簡單的實例演示。我們依然沿用之前關於獲取天氣信息的場景,看看通過這種方式進行注冊的Route對象針對匹配的HTTP請求返回怎樣的RouteData對象。[源代碼從這裡下載]
我們在創建的ASP.NET Web應用(不是ASP.NET MVC應用)添加一個Web頁面(Default.aspx),並按照之前的方式以內聯代碼的方式直接將RouteData的相關屬性顯示出來,頁面主體部分的HTML如下所示。需要注意的是我們顯示的RouteData是從定義的方法GetRouteData方法獲取的,而不是對應於當前頁面的RouteData屬性。
1: <body>
2: <form id="form1" runat="server">
3: <div>
4: <table>
5: <tr>
6: <td>Route:</td>
7: <td><%=GetRouteData().Route != null? GetRouteData().Route.GetType().FullName:"" %></td>
8: </tr>
9: <tr>
10: <td>RouteHandler:</td>
11: <td><%=GetRouteData().RouteHandler != null? GetRouteData().RouteHandler.GetType().FullName:"" %></td>
12: </tr>
13: <tr>
14: <td>Values:</td>
15: <td>
16: <ul>
17: <%foreach (var variable in GetRouteData().Values)
18: {%>
19: <li><%=variable.Key%>=<%=variable.Value%></li>
20: <% }%>
21: </ul>
22: </td>
23: </tr>
24: <tr>
25: <td>DataTokens:</td>
26: <td>
27: <ul>
28: <%foreach (var variable in GetRouteData().DataTokens)
29: {%>
30: <li><%=variable.Key%>=<%=variable.Value%></li>
31: <% }%>
32: </ul>
33: </td>
34: </tr>
35: </table>
36: </div>
37: </form>
38: </body>