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:         routeValues = routeValues ?? new RouteValueDictionary();
  44:         routeValues.Add("action", actionName);
  45:         routeValues.Add("controller", controllerName);
  46:         string virtualPath = this.RouteCollection.GetVirtualPath(this.RequestContext, routeValues).VirtualPath;
  47:         if (string.IsNullOrEmpty(protocol) && string.IsNullOrEmpty(hostName))
  48:         {
  49:             return virtualPath.ToLower();
  50:         }
  51:  
  52:         protocol = protocol??"http";
  53:         Uri uri = this.RequestContext.HttpContext.Request.Url;
  54:         hostName = hostName ?? uri.Host + ":" + uri.Port;            
  55:         return string.Format("{0}://{1}{2}", protocol, hostName, virtualPath).ToLower();
  56:     }
  57: }

RouteHelper具有與UriHelper完全一致的Action方法重載定義,而URL的生成最終體現在最后一個Action重載中。具體的邏輯很簡單,如果指定的Controller名稱為Null,我們通過RequestContext獲取出當前Controller名稱,然后將Action和Controller名稱添加到表示路由變量 列表的RouteValueDictionary對象中(routeValues參數),對應的Key分別是“action”和“controller”

然后我們調用RouteCollection的GetVirtualPath得到一個VirtualPathData對象。如果既沒有顯示指定傳輸協議名稱也沒有指定主機名稱,直接返回VirtualPathData的VirtualPath體現的相對路徑,否則生成一個完整的URL。如果沒有指定主機名稱,我們采用當前請求的主機名稱,並且使用當前的端口;如果沒有指定傳輸協議,則直接使用“http”。

接下來我們在添加的Global.asax中通過如下的代碼注冊一個URL模板為"{controller}/{action}/{id}”的路由對象。

   1: public class Global : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start(object sender, EventArgs e)
   4:     {
   5:         RouteTable.Routes.MapRoute("default", "{controller}/{action}/{id}");
   6:     }
   7: }

在添加的Web頁面(Default.aspx)中我們通過如下的代碼利用我們自定義的RouteHelper生成三個URL。在頁面加載事件處理方法中,我們根據手工創建的HttpRequest和HttpResponse創建一個HttpContext對象,並進一步創建HttpContextWrapper對象。然后我們手工創建一個RouteData對象,並針對上面定義的URL模板添加了三個變量元素(controller=home;action=index;id=002),它們實際上和我們創建的HttpRequest的URL是一一匹配的。最后針對創建的HttpContextWrapper對象和RouteData進一步創建RequestContext對象,並最終創建出RouteHelper對象。

   1: public partial class Default : System.Web.UI.Page
   2: {
   3:     protected void Page_Load(object sender, EventArgs e)
   4:     {
   5:         HttpRequest request = new HttpRequest("default.aspx", "http://localhost:3721/home/index/002", null);
   6:         HttpResponse response = new HttpResponse(new StringWriter());
   7:         HttpContext context = new HttpContext(request, response);
   8:         HttpContextBase contextWrapper = new HttpContextWrapper(context);
   9:  
  10:         RouteData routeData = new RouteData();
  11:         routeData.Values.Add("controller", "home");
  12:         routeData.Values.Add("action", "index");
  13:         routeData.Values.Add("id", "002");
  14:         RequestContext requestContext = new RequestContext(contextWrapper, routeData);
  15:         RouteHelper helper = new RouteHelper(requestContext);
  16:  
  17:         Response.Write(helper.Action("GetProduct", "Products",new {id="002"}) + "<br/>");
  18:         Response.Write(helper.Action("GetProduct", "Products", new { id = "002" }, "http") + "<br/>");
  19:         Response.Write(helper.Action("GetProduct", "Products", new RouteValueDictionary { { "id", "002" } }, 
  20:             "https", "www.artech.com") + "<br/>");
  21:     }
  22: }

我們通過調用RouteHelper其中其中三個Action方法重載生成出三個Url並寫入HTTP回復。對於第一個方法調用,我們指定了Action和Controller的名稱以及針對變量{id}的值;第二次在這基礎上顯示指定了傳輸協議名稱http;第三個在同時指定了協議名稱(https)和主機名稱(www.artech.com)。當我們通過瀏覽器訪問該Web頁面的時候,我們會得到如下圖所示3個URL。

clip_image002

四、UrlHelper.RouteUrl V.S. HtmlHelper.RouteLink

不論是UrlHelper的Action方法,還是HtmlHelper的ActionLink,生成的URL都是通過一個路由表生成出來的,而在默認的情況下這個路由表就是通過RouteTable的靜態屬性Routes表示的全局路由表,換句話說,具體使用的總是路由表中第一個匹配的路由對象。但是在有的時候,我們需要針對注冊的某個具體的路由對象來生成URL或者對應的鏈接,這時候就需要使用的UrlHelper和HtmlHelper的另外一組方法了。

如下面的代碼片斷所示,UrlHelper定義了一系列的RouteUrl方法。除了第一個重載之外,后面的重載都接受一個路由對象注冊名稱的參數routeName。和UrlHelper的Action方法一樣,我們可以通過參數指定用於替換定義在URL模板中變量的RouteValueDictionary對象(routeValues),以及傳輸協議和主機名稱(hostName)。

   1: public class UrlHelper
   2: {
   3:     //其他成員
   4:     public string RouteUrl(object routeValues);
   5:     public string RouteUrl(string routeName);
   6:     public string RouteUrl(RouteValueDictionary routeValues);
   7:     public string RouteUrl(string routeName, object routeValues);
   8:     public string RouteUrl(string routeName, RouteValueDictionary routeValues);
   9:     public string RouteUrl(string routeName, object routeValues, string protocol);
  10:     public string RouteUrl(string routeName, RouteValueDictionary routeValues, string protocol, string hostName);  
  11: }

對於沒有指定路由對象注冊名稱的RouteUrl方法來說,它還是利用整個路由表進行URL的生成,如果顯示指定了路由對象的注冊名稱,那么就會從路由表中獲取相應的路由對象,如果該路由對象與指定的變量列表不匹配,則返回Null;否則返回生成的URL。

HtmlHelper也同樣定義了類似的RouteLink方法重載用於實現基於指定路由對象的鏈接生成,具體的RouteLink方法定義如下。

   1: public static class LinkExtensions
   2: {
   3:     //其他成員
   4:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, object routeValues);
   5:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName);
   6:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, RouteValueDictionary routeValues);
   7:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, object routeValues, object htmlAttributes);
   8:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, RouteValueDictionary routeValues);
   9:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes);
  10:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, object routeValues, object htmlAttributes);
  11:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes);
  12:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, string protocol, string hostName, string fragment, object routeValues, object htmlAttributes);
  13:     public static MvcHtmlString RouteLink(this HtmlHelper htmlHelper, string linkText, string routeName, string protocol, string hostName, string fragment, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes);
  14: }

ASP.NET MVC路由擴展:路由映射
ASP.NET MVC路擴展:鏈接和URL的生成


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM