接着前面繼續學習分享我們的路由。
現在我們把Global.asax文件里的RegisterRoutes方法還原至原來的樣式,具體代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Routing.Infrastructure; namespace Routing { // 注意: 有關啟用 IIS6 或 IIS7 經典模式的說明, // 請訪問 http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { //整理RegisterRoutes routes.MapRoute("MyRoute", "{controller}/{action}/id", new { controller = "Home", action = "Index", id = UrlParameter.Optional }); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); } } }
生成的URL在視圖
生成傳出URL最簡單的方法是在視圖里調用內的Html.ActionLink方法。具體代碼如下:
@Html.ActionLink("About this application", "About")
ActionLink的方法的參數為文本鏈接和操作方法的名稱,應針對鏈接。ActionLink的方法生成的HTML的基礎上,在當前的路由架構。上面運行后的HTML代碼表示如下:
<a href="/Home/About">About this application</a>
但假設我們改變路由模式通過添加一個新的路由,像下面:
public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("NewRoute", "App/Do{Action}", new { controller = "Home" }); //整理RegisterRoutes routes.MapRoute("MyRoute", "{controller}/{action}/id", new { controller = "Home", action = "Index", id = UrlParameter.Optional }); }
然后我們得到了下面的HTML從ActionLinkhelper方法當我們呈現頁面:
<a href="/App/DoAbout">About this application</a>
在這種方式生成的鏈接如何解決維修問題。我們能夠改變我們的路由模式,即將生成自動映射這一變化。
指向其他控制器(Controller)
默認的版本的ActionLink方法,假定您想要一個動作方法為目標在同一個控制器已經造成視圖呈現出來。創建一個新生成的URL,目標是不同的控制器您可以使用一個不同的重載,它允許您指定控制器的名字,具體代碼如下:
@Html.ActionLink("About this application", "About", "MyController")
當你運行器項目,你會看到他的連接會是這樣的,具體代碼如下:
<a href="/MyController/About">About this application</a>
傳遞額外的值
我們可以通過段使用匿名類型的變量的值,用屬性代表段,具體如下:
@Html.ActionLink("About this application", "About", new { id = "MyID" })
我們已經提供了一個值為一段變量稱為id。當我跑起項目,我們可以看到這樣的連接,具體如下:
<a href="/Home/About/MyID">About this application</a>
我們提供的參數值已經添加作為URL的細分部分,以匹配的URL模式到我們的應用程序的路徑。
當我們提供的不符合部分變量的屬性值,值作為查詢字符串附加到傳出的URL,具體代碼如下:
@Html.ActionLink("About this application", "About", new { id = "MyID", myVariable = "MyValue"})
他生成的HTML代碼如下:
<a href="/Home/About/MyID?myVariable=MyValue">About this application</a>
如果我們提供一個變量值,正好符合我們在路由指定的默認值,然后路由系統省略了即將生成輸出的URL變量,具體代碼如下:
@Html.ActionLink("About this application", "Index", "Home")
我們作為參數傳遞的值的操作方法和控制器的匹配默認,運行后生成頁面的HTML代碼如下:
<a href="/">About this application</a>
指定的HTML屬性
生成一個完整的HTML錨(<A>)的元素。我們可以通過提供一個設置該元素的屬性匿名類型的屬性對應到我們所需要的屬性。下面一個示范設置一個id屬性和分配的HTML元素的CSS類。
(生成一個錨元素和屬性).具體代碼如下:
@Html.ActionLink("About this application", "Index", "Home", null, new {id = "myAnchorID", @class = "myCSSClass"})
我們已經創建了一個新的匿名類型的id和class屬性,並通過它作為一個ActionLink的方法參數。我們通過額外的變量值部分為null,表明我們沒有提供任何值,運行后的HTML代碼如下:
<a class="myCSSClass"href="/" id="myAnchorID">About this application</a>
生成一個完全合格的URL
到目前為止,我們已經產生含有相對URL,但我們也可以使用ActionLinkhelper方法來生成一個合格的URL,具體代碼如下:
@Html.ActionLink("About this application", "Index", "Home", "https", "myserver.mydomain.com", " myFragmentName", new { id = "MyId"}, new { id = "myAnchorID", @class = "myCSSClass"})
這是ActionLink的載入大多數參數,它提供的值,目標服務器的名稱(myserver.mydomain.com)和URL片段(myFragmentName),以及你以前看到的所有其他選項。當呈顯在視圖,運行之后的HTML如下:
<a class="myCSSClass" href="https://myserver.mydomain.com/Home/Index/MyId#myFragmentName" id="myAnchorID">About this application</a>
我們建議盡可能使用相對URL。完全合格的URL創建依賴關系的方式呈現給用戶,您的應用程序基礎設施。我們已經看到了許多大應用程序依賴不協調的變化打破了網絡的絕對URL基礎設施或域名政策,這也是我們無法管控的。
生成URL(非連接)
Html.ActionLink輔助方法生成完整的HTML<a>元素,這是正是我們想要的。然而,有的時候,我們只需要一個URL,這可能是因為我們要顯示的URL,建立一個鏈接的HTML手動顯示的URL值,或包含作為一個數據元素所呈現的HTML頁面的網址。
在這種情況下,我們可以使用生成的URL,而不是Url.Action方法周圍的HTML,具體代碼如下:
... My URL is: @Url.Action("Index", "Home", new { id = "MyId" }) ...
Url.Action方法的Html.ActionLink方法相同的方式工作,但它生成唯一的URL。運行后的HTML代碼如下:
My URL is: /Home/Index/MyId
路由數據生成的鏈接和URL
前面提到,路由系統不指定任何特殊的意義控制器行動部分的變量,當它被處理的URL。附加到MVC框架,允許路由系統可以更廣泛地與其他類型的ASP.NET應用程序使用。有時它是用於對待控制器和行動,就像任何其他變量。並產生鏈接或URL提供一個 Name/Value 對的集合。我們可以通過使用輔助方法不是具體的mvc特性。
下面看 生成一個鏈接使用匿名類型,具體代碼如下:
@Html.RouteLink("Routed Link", new { controller = "Home", action = "About", id="MyID"})
有沒有參數,為RouteLink方法來表達控制器和行動值。我們必須包括他們作為匿名類型的屬性。運行后的HTML代碼如下:
<a href="/Home/About/MyID">Routed Link</a>
我們也可以使用 Url.RouteUrl輔助方法來生成網址,具體代碼如下:
@Url.RouteUrl(new { controller = "Home", action = "About", id = "MyID" })
這些方法都很少需要,因為我們一般都知道,要指定控制器和行動明確的值。但它是很好的了解,這些方法都存在,當你需要他們輕松很多,但是很少可能。
在操作(Action)方法生成傳出URL
大多數情況下,我們需要生成傳出網址,但有時,當我們想做些什么類似的內部操作方法。如果我們只需要生成一個URL,我們可以使用相同的輔助方法我們在視圖中使用,具體代碼如下:
public ViewResult MyActionMethod() { string myActionUrl = Url.Action("Index", new { id = "MyID" }); string myRouteUrl = Url.RouteUrl(new { controller = "Home", action = "Index" }); ... }
一個更常見的需求是重定向客戶端瀏覽器到另一個URL。我們可以通過調用RedirectToAction方法,例如下面代碼:
public ActionResult MyActionMethod() { return this.RedirectToAction("Index"); }
RedirectToAction方法的結果是RedirectToRouteResult,指示的MVC框架發出重定向的URL將調用指定的動作指令。還有一些通常RedirectToAction方法的重載版本,指定控制器和值在生成的URL段的變量。如果你想發送重定向使用從對象的屬性生成一個URL,你可以使用RedirectToRoute方法,(重定向到一個URL生成一個匿名類型的屬性),具體代碼如下:
public ActionResult MyOtherActionMethod() { return RedirectToRoute(new { controller = "Home", action = "Index", id = "MyID" }); }
這種方法也返回一個RedirectToRouteResult對象,並調用具有完全相同的效果RedirectToAction方法.
從一個特定的路由生成一個URL
例如,我們定義下面的路由:
- routes.MapRoute("MyRoute", "{controller}/{action}");
- routes.MapRoute("MyOtherRoute", "App/{action}", new { controller = "Home" });
我們作為第一個參數傳遞的路由,我們創建指定名稱的MapRoute方法這種情況下,MyRoute和MyOtherRoute。
命名你的路由有兩個原因:
- 作為一種提醒路由的目的
- 你可以選擇一個特定的路由被用來生成一個傳出的URL
我們已安排的路由,使最不特定的列表中首先出現。這意味着,如果我們要生成一個鏈接使用這樣的ActionLink的方法,具體代碼如下:
@Html.ActionLink("Click me", "About");
你可以通過默認路由匹配的行為使用Html.RouteLink方法,這讓你指定你要使用哪條路線,具體代碼如下:
@Html.RouteLink("Click me", "MyOtherRoute", new { action = "About" });
★定制路由體制★
我們已經看到如何靈活和可配置的路由系統,但如果它不符合我們的的要求,那么我們可以自定義的行為。
創建自定義RouteBase實現
如果你不喜歡標准Route對象匹配的URL的方式,或想實現的東西不尋常的,可以替代類從RouteBase派生。這使你可以匹配URL,參數提取,並傳出的URL是如何傳出的。
從RouteBase得出一個類,您需要實現兩個方法:
- GetRouteData(HttpContextBase httpContext):這是該機制匹配入站URL。框架調用此方法對每個反過來RouteTable.Routesentry,直到其中一個返回一個非的NullValue。
- GetVirtualPath(RequestContext requestContext, RouteValueDictionary values):這是路由工作匹配出站URL生成的機制。 框架調用方法上每個反過來RouteTable.Routesentry,直到之一他們返回一個非的NullValue。
為了證明這種定制,我們要創建一個RouteBase類將處理傳統的URL請求。試想一下,我們現有的應用程序遷移到MVC框架,但有些MVC URL或硬編碼成劇本。我們仍然希望支持這些舊的網址。我們可以處理這種使用常規的路由機制。開始,我們需要創建一個控制器,我們將在收到我們的傳統要求。我們需要通知給我們控制器LegacyController(這里從新創建一個項目"Routing_Project"),具體代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace Routing_Project.Controllers { public class LegacyController : Controller { public ActionResult GetLegacyURL(string legacyURL) { return this.View((object)legacyURL); } } }
在這個簡單的控制器的GetLegacyURL操作方法需要的參數,並通過它作為一個查看視圖的模型。如果我們真的實現此控制器,我們將使用此方法檢索請求的文件,因為我們只是要在一個視圖中顯示的URL。對應的視圖GetLegacyURL.cshtml代碼如下:
@model string @{ ViewBag.Title = "GetLegacyURL"; this.Layout = null; } <h2>GetLegacyURL</h2> The URL Requested Was:@Model
這是很簡單的。我們只要證明自定義的路由行為,所以我們不要花費任何時間創建復雜的行動和視圖。現在,我們已經達到目的,我們可以創建派生RouteBase。
路由傳入的URL
我們已創建LegacyRoute類,這是我們放在頂層文件夾("Infrastructure").具體代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace Routing_Project.Infrastructure { public class LegacyRoute : RouteBase { private string[] urls; //構造函數 public LegacyRoute(params string[] targetUrls) { this.urls = targetUrls; } public override RouteData GetRouteData(HttpContextBase httpContext) { RouteData result = null; //獲取應用程序的虛擬目錄 string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath; if (this.urls.Contains(requestedURL,StringComparer.OrdinalIgnoreCase)) { result = new RouteData(this, new MvcRouteHandler()); result.Values.Add("controller", "Legacy"); result.Values.Add("action", "GetLegacyURL"); result.Values.Add("legacyURL", requestedURL); } return result; } public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { return null; } } }
這個類的構造函數接受一個字符串數組,代表網址將支持路由類。我們會指定這些當我們以后注冊的路由。GetRouteData方法,這就是路由系統調用來看看我們是否能夠處理傳入URL。
如果我們不能處理的要求,那么我們可以直接返回null,路由機制將移動到列表中的下一個路由並重復這個過程。如果我們能夠處理的請求,我們需要返回一個RouteData類包含控制器和操作變量的值的實例,別的我們要傳遞的操作方法。接下來我們需要注冊一條路由,具體的代碼在Global.asax添加如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using Routing_Project.Infrastructure; namespace Routing_Project { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //注冊一條路由 routes.Add(new LegacyRoute("~/articles/Windows_3.1_Overview.html", "~/old/.NET_1.0_Class_Library")); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); } protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); } } }
我們創建一個新的類的實例傳遞的URL希望它來自路由。然后,我們添加RouteCollection使用Add方法。現在,當我們要求舊的URL在我們自定義的類,路由請求和導演對我們的控制器如下圖1所示。
圖1.
生成外向的URL
為了支持即將向外生成的URL,我們需要實現的GetVirtualPath方法。如果我們無法處理請求,我們讓路由系統知道返回null。否則,我們返回一個的VirtualPathData類的實例。具體修改如下代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace Routing_Project.Infrastructure { public class LegacyRoute : RouteBase { private string[] urls; //構造函數 public LegacyRoute(params string[] targetUrls) { this.urls = targetUrls; } public override RouteData GetRouteData(HttpContextBase httpContext) { RouteData result = null; //獲取應用程序的虛擬目錄 string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath; if (this.urls.Contains(requestedURL,StringComparer.OrdinalIgnoreCase)) { result = new RouteData(this, new MvcRouteHandler()); result.Values.Add("controller", "Legacy"); result.Values.Add("action", "GetLegacyURL"); result.Values.Add("legacyURL", requestedURL); } return result; } public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { VirtualPathData result = null; if (values.ContainsKey("legacyURL")&& this.urls.Contains((string)values["legacyURL"],StringComparer.OrdinalIgnoreCase)) { result = new VirtualPathData(this, new UrlHelper(requestContext).Content((string)values["legacyURL"]).Substring(1)); } return result; } } }
我們已經通過細分變量和其他細節在使用匿名類型,但幕后,路由系統已RouteValueDictionary對象轉換成這些。因此我們添加這么一個視圖代碼如下:
@Html.ActionLink("Click me", "GetLegacyURL", new { legacyURL = "~/articles/Windows_3.1_Overview.html" })
創建與legacyURL屬性的匿名類型轉換成RouteValueDictionary類包含同名的關鍵。在這個例子中,我們決定,我們可以處理一個請求外出URL,如果有名為legacyURL一個關鍵,如果它的值是一個URL,被傳遞到構造。我們可以更具體和檢查控制器和行動值,在簡單的應用中這些應該可以滿足的!
創建一個自定義路由處理程序
我們依靠MvcRouteHandler在我們的路由,因為它連接到MVC的路由機制。由於我們的重點是MVC框架,這是我們想要的幾乎所有的時間關注的。既然如此,路由機制,讓我們定義我們自己的路由處理實施IRouteHandler接口。具體代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Routing; namespace Routing_Project.Infrastructure { public class CustomRouteHandler : IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { return new CustomHttpHandler(); } } public class CustomHttpHandler : IHttpHandler { public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { context.Response.Write("Hello"); } } }
接口的IRouteHandler的目的是提供一種方法來生成的實現IHttpHandler接口,這是負責處理請求。在MVC實現控制器被發現,這些接口,調用的操作方法,觀點呈現,結果
被寫入響應。我們的實現是簡單一點。它只是寫的字打招呼客戶端(不含有這個詞的一個HTML文件,但只是文本)。OK,我們依然還是需要注冊路由,具體如下:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //注冊一條路由 routes.Add(new LegacyRoute("~/articles/Windows_3.1_Overview.html", "~/old/.NET_1.0_Class_Library")); //使用一個自定義路由處理器的路線 routes.Add(new Route("SayHello", new CustomRouteHandler())); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); }
運行項目,我們訪問“ URL /SayHello”,結果如下圖2
圖2.
處理領域
MVC框架支持組織一個web應用程序到的區域,每個區域代表應用程序的功能性組比如賬單、客戶支持,等等,這在一個大的項目是非常有用的,那里有一套單一的文件夾,所有的控制器,視圖和模型可以變得難以管理。每個MVC區域是有自己的文件夾結構,允許您分開管理。這使得它更顯而易見哪個項目元素相互關聯應用程序的功能區域,這有助於多個開發人員同事處理項目而沒有彼此胡想不干擾。區域是支持主要通過路由機制。
我們從新新建一個MVCweb應用程序("MVCArea"),創建好項目,我們直接演示怎么給項目添加一個區域進來具體如下圖3.-4.
圖3.
圖4.當我們創建好我們的區域之后項目的結構會變成如下圖5所示。
圖5.OK這里你可以看一套類似與MVC文件機制東東出來,這個就是我們創建的區域關於這個東西后面慢慢來學習他,我們現在只關心他那個自動創建AdminAreaRegistration.cs的文件里面是怎么一回事,不看內容就憑着這個名字猜一下,大概就路由扯上關系了。OK,打開看看,AdminAreaRegistration.cs的代碼如下:
using System.Web.Mvc; namespace MvcArea.Areas.Admin { public class AdminAreaRegistration : AreaRegistration { public override string AreaName { get { return "Admin"; } } public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "Admin_default", "Admin/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); } } }
OK,可以看出上面代碼我加粗標識的RegisterArea方法十分有意思,在這個區域里注冊一個路由的URL模式" Admin/{controller}/{action}/{id}",當然我們可以在這里定義其他的URL模式,但是你要知道你這這里定義的話只在該區域里有效,也就是你額外定義的路由機制的持有權在該區域內。
我們不需要采取任何行動來確保這個注冊方法被調用。因為Global.asax文件里的Application_Start方法在我們創建區域的時候為自動為我們注冊進去,那么我們來打開Global.asax文件里的Application_Start方法看看,具體代碼如下:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); }
可以看到是OK的,接下我們在我們創建的Admin區域里添加一個控制器("Controller")和方法("Action")和一些視圖("View")來看看效果,首先添加一個控制器("Controller"),如下圖6.
圖6.創建HomeController,具體的代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcArea.Areas.Admin.Controllers { public class HomeController : Controller { // // GET: /Admin/Home/ public ActionResult Index() { return this.View(); } } }
OK,我們在創建一個視圖,如下圖7.
圖7。創建好Index.cshtml,頁面的代碼如下:
@{ ViewBag.Title = "Index"; } <h2>Admin Area Index</h2>
OK,我們試着運行一下我們的Web項目,並且訪問一下他的路由" /Admin/Home/Index",結果如下圖8.
圖8.
貌似我們撒了一個謊言,我們試着訪問一下我們web項目的根路徑看看什么效果,運行如下圖9.
圖9.
OK,MVC路由機制一下找到了2個"HomeController"這下不知道去那個,就出錯了!
當一個地區注冊,任何路由,我們定義僅限於名稱空間關聯該區域。這就是我們為什么請求" /Admin/Home/Index "的時候。路由機制找到"HomeController"的命名空間MvcArea.Areas.Admin.Controllers。但是 Global.asax文件的機制可不是這個樣子的,來看看我們默認的 Global.asax文件是怎么搞的,具體代碼如下:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); }
名為default的路由轉換傳入的URL從瀏覽器到主控制器HomeController行動上Index方法。在這一點上,我們得到一個錯誤,因為有沒有命名空間的限制這條路線和MVC框架,可以看到兩個的HomeController類。所以我們就可以看到上面悲劇發生,我們對Global.asax文件做簡單的處理,具體操作如下:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults new[] { "MvcArea.Controllers" } ); }
這種變化,確保在控制器的主要項目,給予優先解決的請求。
MVC框架中檢測到當前的請求與特定的區域, 然后出站的URL生成將會找到一個匹配只有在路線定義為該領域。比如在我們的Index.cshtml修改如下代碼所示:
@{ ViewBag.Title = "Index"; } <h2>Admin Area Index</h2> @Html.ActionLink("Cilck me", "About")
運行項目,可以看到如下圖10的生成的連接.
圖10.
當然也可以修改如下代碼所示(創建一個鏈接到一個動作在不同的區域,或沒有區域內,您必須創建一個變量調用地區並使用它來指定區域的名字你想要的):
@{ ViewBag.Title = "Index"; } <h2>Admin Area Index</h2> @Html.ActionLink("Cilck me", "About") <br /> @Html.ActionLink("Click me to go to another area", "Index", new { area = "Support" })
運行的效果如下圖11.
圖11.OK,關於路由就西安分享到這里(后續遇到什么在繼續補充吧),文章要是那里有描述有誤的地方,還請各位前輩多多批評指導,大家共同學習!