1. 程式人生 > >ASP.NET MVC路由擴充套件:連結和URL的生成

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物件的封裝

。它們分別對應於只讀屬性RequestContext和RouteCollection,並且在建構函式中被初始化。如果在構造UrlHelper的時候沒有指定RouteCollection物件,那麼通過RouteTable的靜態屬性Routes表示的全域性路有表將直接被使用。

   1: public class UrlHelper
   2: {
   3:     //其他成員
   4:     public UrlHelper(RequestContext requestContext);
   5:     public
UrlHelper(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: