ASP.NET MVC路由擴充套件:連結和URL的生成
ASP.NET 路由系統通過註冊的路由表旨在實現兩個“方向”的路有功能,即針對入棧請求的路由和出棧URL的生成。前者通過呼叫代表全域性路由表的RouteCollection物件的GetRouteData方法實現,後者則依賴於RouteCollection的GetVirtualPathData方法,而最終還是落在繼承自RouteBase的路由物件的同名方法的呼叫上。為了程式設計的方面,ASP.NET MVC為了設計了HtmlHelper和UrlHelper這兩個幫助類,我們可以通過呼叫它們的ActionLink/RouteLink和Action/RouteUrl根據註冊的路有規則生成連結或者URL。從本質上講,HtmlHelper/UrlHelper實現的對URL的生成最終還是依賴於上面所說的GetVirtualPathData方法。
目錄
一、UrlHelper V.S. HtmlHelper
二、UrlHelper.Action V.S. HtmlHelper.ActionLink
三、例項演示:建立一個RouteHelper模擬UrlHelper的URL生成邏輯
四、UrlHelper.RouteUrl V.S. HtmlHelper.RouteLink
一、UrlHelper V.S. HtmlHelper
在介紹如果通過HtmlHelper和UrlHelper來生成連結或者URL之前,我們來先來看看它們的基本定義。從下面給出的程式碼片斷我們可以看出UrlHelper物件實際上對一個表示請求上下文的RequestContext和路由物件集合的RouteCollection物件的封裝
1: public class UrlHelper
2: {
3: //其他成員
4: public UrlHelper(RequestContext requestContext);
5: publicUrlHelper(RequestContext requestContext, RouteCollection routeCollection);
6:
7: public RequestContext RequestContext { get; }
8: public RouteCollection RouteCollection { get;}
9: }
再來看看如下所示的HtmlHelper的定義,它同樣具有一個表示路由物件集合的RouteCollection屬性。和UrlHelper一樣,如果在建構函式沒有顯示指定,那麼RouteTable的靜態屬性Routes表示的RouteCollection物件將會用於初始化該屬性。
1: public class HtmlHelper
2: {
3: //其他成員
4: public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer);
5: public HtmlHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection);
6:
7: public RouteCollection RouteCollection { get; }
8: public ViewContext ViewContext { get; }
9: }
10: public class ViewContext : ControllerContext
11: {
12: //省略成員
13: }
14: public class ControllerContext
15: {
16: //其他成員
17: public RequestContext RequestContext { get; set; }
18: public virtual RouteData RouteData { get; set; }
19: }
由於HtmlHelper只要在View中使用,所以它具有一個通過ViewContext屬性表示的針對View的上下文。至於該屬性對應的型別ViewContext,它是表示Controller上下文的ControllerContext的子類,而後者通過RequestContext和RouteData屬性提供當前的請求上下文和路由資料(其實RouteData屬性表示的RouteData物件已經包含在RequestContext屬性表示的RequestContext物件中)。
二、UrlHelper.Action V.S. HtmlHelper.ActionLink
UrlHelper和HtmlHelper分別通過Action和ActionLink方法用於生成一個針對某個Controller的某個Action的URL和連結。下面的程式碼片斷列出了UrlHelper的所有Action過載,引數actionName和controllerName分別代表Action和Controller的名稱。通過object或者RouteValueDictionary型別表示的routeValues引數表示替換URL模板中變數的變數值。引數protocol和hostName代表作為完整URL的傳輸協議(比如http和https等)以及主機名。
1: public class UrlHelper
2: {
3: //其他成員
4: public string Action(string actionName);
5: public string Action(string actionName, object routeValues);
6: public string Action(string actionName, string controllerName);
7: public string Action(string actionName, RouteValueDictionary routeValues);
8: public string Action(string actionName, string controllerName, object routeValues);
9: public string Action(string actionName, string controllerName, RouteValueDictionary routeValues);
10:
11: public string Action(string actionName, string controllerName, object routeValues, string protocol);
12: public string Action(string actionName, string controllerName, RouteValueDictionary routeValues, string protocol, string hostName);
13: }
對於定義在UrlHelper中的眾多Action方法,如果我們顯示指定了傳輸協議(protocol引數)或者主機名稱,返回的是一個完整的URL;否則返回的是一個相對URL。如果我們沒有顯示地指定Controller的名稱(controllerName引數),那麼當前Controller的名稱被採用。對於UrlHelper來說,通過RequestContext屬性表示的當前請求上下文包含了相應的路由資訊,即RequestContext的RouteData屬性表示的RouteData。RouteData的Values屬性中必須包含一個Key為“controller”的元素,其值就代表當前Controller的名稱。
在System.Web.Mvc.Html.LinkExtensions中,我們為HtmlHelper定義瞭如下所示的一系列ActionLink方法過載。顧名思義,ActionLink不再僅僅返回一個URL,而是生成一個連結(<a>...</a>),但是其中作為目標URL的生成邏輯和UriHelper是完全一致的。
1: public static class LinkExtensions
2: {
3: //其他成員
4: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName);
5: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues);
6: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName);
7: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues);
8: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, object routeValues, object htmlAttributes);
9: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes);
10: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes);
11: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName,RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes);
12: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, object routeValues, object htmlAttributes);
13: public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes);
14: }
三、例項演示:建立一個RouteHelper模擬UrlHelper的URL生成邏輯
為了讓讀者對UrlHelper如果利用ASP.NET路由系統進行URL生成的邏輯具有一個深刻認識,我們接下來建立一個名為RouteHelper的等效幫助類。我們將RouteHelper定義在建立的一個ASP.NET Web應用中,如下面的程式碼片斷所示,RouteHelper具有RequestContext和RouteCollection兩個屬性,前者在建構函式中指定,後者則只是使用通過RouteTable的Routes靜態屬性表示的全域性路由表。原始碼從這裡下載。
1: public class RouteHelper
2: {
3: public RequestContext RequestContext { get; private set; }
4: public RouteCollection RouteCollection { get; private set; }
5: public RouteHelper(RequestContext requestContext)
6: {
7: this.RequestContext = requestContext;
8: this.RouteCollection = RouteTable.Routes;
9: }
10:
11: public string Action(string actionName)
12: {
13: return this.Action(actionName, null, null, null, null);
14: }
15: public string Action(string actionName, object routeValues)
16: {
17: return this.Action(actionName, null,
18: new RouteValueDictionary(routeValues), null, null);
19: }
20: public string Action(string actionName, string controllerName)
21: {
22: return this.Action(actionName, controllerName, null, null, null);
23: }
24: public string Action(string actionName, RouteValueDictionary routeValues)
25: {
26: return this.Action(actionName, null, routeValues, null, null);
27: }
28: public string Action(string actionName, string controllerName, object routeValues)
29: {
30: return this.Action(actionName, controllerName, new RouteValueDictionary(routeValues), null, null);
31: }
32: public string Action(string actionName, string controllerName, RouteValueDictionary routeValues)
33: {
34: return this.Action(actionName, controllerName, routeValues, null, null);
35: }
36: public string Action(string actionName, string controllerName, object routeValues, string protocol)
37: {
38: return this.Action(actionName, controllerName, new RouteValueDictionary(routeValues), protocol, null);
39: }
40: public string Action(string actionName, string controllerName, RouteValueDictionary routeValues, string protocol, string hostName)
41: {
42: controllerName = controllerName ?? (string)this.RequestContext.RouteData.Values["controller"];
43: