ASP.NET MVC架構(第二部分): URL直接選取

來源:互聯網
上載者:User

【原文地址】ASP.NET MVC Framework (Part 2): URL Routing
【原文發表日期】 Monday, December 03, 2007 2:44 AM

上個月,我發表了我要撰寫的系列貼子中的第一篇,這些文章將討論我們正在開發的新ASP.NET MVC架構。這個系列的第一個貼子建造了一個簡單的電子商務產品列表/瀏覽情境,討論了MVC後面的高層次的概念,示範了如何從頭建立一個新ASP.NET MVC 項目,實現和測試電子商務產品列表的功能。

在今天的貼子裡,我將深入討論ASP.NET MVC架構的直接選取(routing)架構,討論一些很酷的定製方式,你可以將其用於你應用中的一些更進階的情境。

第一部分的扼要簡述

在這個系列的第一部分裡,我們建立了一個電子商務網站,呈示了三類URL:

URL格式 行為 URL例子
/Products/Categories 瀏覽所有的產品分類 /Products/Categories
/Products/List/Category 列出一個分類中的產品 /Products/List/Beverages
/Products/Detail/ProductID 顯示一個特定產品的細節 /Products/Detail/34

我們通過建立象下面這樣一個ProductsController類來處理這些URL:

在把上面這個類加到我們的應用中後,ASP.NET MVC架構就會把進來的URL自動導向到我們的控制器上的適當的action方法來處理請求。

在今天的貼子裡,我們將深入討論這個URL映射是如何發生的,以及探討我們可以在ASP.NET MVC架構中利用的更進階的直接選取(routing)情境。我還將示範你如何可以輕鬆地單元測試URL直接選取情境。

ASP.NET MVC URL直接選取系統都做些什嗎?

ASP.NET MVC架構套件括了一個很靈活的URL直接選取系統,它允許你在應用中定義URL映射規則。直接選取系統有2個主要目的:

  1. 把進來的URL映射到應用,並把它們做導向,這樣,正確的Controller和Action方法執行來處理這些請求
  2. 構建可以用來回調Controllers/Actions的輸出到用戶端的URL(例如,表單提交, <a href=""> 連結, 和 AJAX 調用等等)

能夠使用URL映射規則來同時處理進來的和輸出的URL情境給應用代碼添加了許多靈活性。這意味著,如果我們以後想改變應用的URL結構的話(譬如,把 /Products 改名為 /Catalog),我們可以修改應用程式層次的一套映射規則即可,而不需要改動控制器或視圖模板中的任何代碼。

預設的ASP.NET MVC URL直接選取規則

在預設情形下,當你使用Visual Studio用ASP.NET MVC Web Application模板來建立一個新項目時,它會往項目裡添加一個ASP.NET Application類。這是在Global.asax後台代碼中實現的:

ASP.NET Application類允許開發人員處理應用啟動/中止以及全域性的錯誤處理的邏輯。

預設的ASP.NET MVC項目模板自動向該類添加一個Application_Start方法,在其中註冊2條URL直接選取規則:

上面的第一條直接選取規則表示,ASP.NET MVC架構在預設情形下,在決定用哪個Controller類來產生執行個體,調用哪個Action方法時(以及哪些需要傳入的參數時),應該使用" [controller]/[action]/[id]"的格式把URL映射到控制器上。

這個預設的直接選取規則就是為什麼第一部分中我們的電子商務瀏覽常式中對URL /Products/Detail/3 的請求自動調用我們的ProductsController類的Detail方法,並且傳入3作為id參數值的原因:

上面的第二條直接選取規則,是用來對我們應用的根URL"Default.aspx"做特例處理的(當處理一個應用的根URL的請求時,這個URL有時會被伺服器代替"/"來傳入)。這個規則確保對我們應用的根"/Default.aspx"或"/"的請求,都會由HomeController類(是在我們使用ASP.NET MVC Web Application項目模板產生一個新的應用時,由Visual Studio自動產生的控制器)裡的Index() action方法處理。

理解Route執行個體

直接選取規則是通過向System.Web.Mvc.RouteTable的Routes集合添加Route執行個體來註冊的。

Route類定義了許多你可以用以配置映射規則的屬性。你可以通過“傳統的” .NET 2.0屬性賦值的方式來設定這些屬性:

或者利用VS 2008的C#和VB編譯器中的新的對象初始化器特性,更簡潔地設定屬性:

 

Route 類的Url屬性定義了應該用來評估一個直接選取規則是否適用於進來的特定請求的Url匹配規則。它還定義了URL應該如何分割成(tokenized)不同的參數。URL中可替換的參數,是通過 [參數名稱] 的句法來定義的。就象在後文論及的那樣,我們並不限制於一套固定的“熟知”參數名稱,你可以在URL使用任何數目的任意參數。例如,我可以使用一個 "/Blogs/[Username]/Archive/[Year]/[Month]/[Day]/[Title]"的URL規則把進來的一個部落格貼子的URL進行分割,由MVC架構自動分析成UserName,Year,Month,Day 和 Title參數,並把它們傳入我的控制器的action方法中。

Route類上的Defaults屬性定義了一個預設值的字典,可以在進來的URL並不包含某個指定的參數值的情形下使用。例如,在上面的URL映射例子中,我們定義了2個預設URL參數值,一個是"[action]" ,另一個是 "[id]"。這意味著,如果應用收到的是 /Products/ 這個URL,在預設情形下,直接選取系統會預設使用“Index”作為ProductsController的action的名稱來執行。同樣地,如果指定了/Products/List/ ,那麼就會使用null字串作為"ID"參數的值。

Route類的RouteHandler 屬性定義了在URL被分割成參數,適當的直接選取規則被確定之後,應該用來處理請求的 IRouteHandler 執行個體。在上面的例子中,我們表示,我們想要使用System.Web.Mvc.MvcRounteHandler類來處理我們配置好的URL。這個額外的步驟存在的原因是,我們想確保URL直接選取系統可以同時用於MVC和非MVC請求的情形。有這個IRouteHandler介面,意味著,我們也能夠乾淨地用於非MVC的請求(例如標準的WebForms,Astoria REST支援等等)。

Route類還有一個 Validation屬性,在本文的稍後我們會做討論。這個屬性允許我們指定一個直接選取規則匹配需要滿足的先決條件。例如,我們可以指定一個直接選取規則應該只適用於一個特定的HTTP動詞(允許我們輕鬆地映射REST命令),或者我們可以對參數值使用Regex,來過濾一個直接選取規則是否匹配。

註:在MVC架構的第一個公開預覽版中,Route類是不可以擴充的(它只是個資料類),在下一個預覽版中,我們正在研究把它做成可擴充的,允許開發人員添加特定情境的路徑類(譬如,一個RestRoute子類)來乾淨利索地添加新的語義和功能。

路徑規則的評估

當一個進來的URL被ASP.NET MVC Web應用收到時, MVC架構會對RouteTable.Routes集合中的直接選取規則進行評估,以決定適當的Controller來處理該請求。

MVC 架構是按RouteTable規則註冊的次序做評估來選擇使用哪個Controller的。將進來的URL對每條Route規則做檢測,看它是否匹配,如果一個Route規則匹配的話,那麼該規則(以及相關聯的RouteHandler)將被用來處理進來的請求(所有後面的規則都略過不計)。這意味著你一般要按“最特殊到最不特殊(most specific to least specific,從特殊到一般)”的次序來組織你的直接選取規則。

直接選取情境:自訂查詢URL

讓我們使用一下現實情境中的自訂直接選取規則來對此做一流程示範,以實現我們的電子商務網站的查詢功能為例。

開始,我們往我們項目中添加一個新的SearchController類:

然後,我們在SearchController類中定義2個Action方法。Index()方法用來顯示一個查詢網頁,上有一個文字框,讓使用者來輸入和提交查詢文字。Results() action方法則用來處理相應的表單提交,對資料庫做查詢,然後把結果顯示給使用者:

 

使用預設的/[controller]/[action]/[id] URL路徑映射規則,我們可以現成使用象下面這樣的URL來調用我們的SearchController的行為:

情境 URL Action方法
查詢表單: /Search/ Index
查詢結果: /Search/Results?query=Beverages Results
  /Search/Results?query=ASP.NET Results

注意,根URL /Search 預設映射到Index() action方法的原因是因為在Visual Studio建立一個新項目時,預設添加的 /[controller]/[action]/[id] 的路徑定義將預設的action自動化佈建到“Index"上的(通過Defaults屬性):

雖然象 /Search/Results?query=Beverages 這樣的URL是完全可行的,我們也許決定對查詢結果我們想要稍微好看些的URL。具體來說,我們也許想去掉URL中的“Results”action名稱,把要查詢的文字作為URL的一部分傳入,而不是作為URL的查詢字串的值。例如:

情境 URL Action方法
查詢表單: /Search/ Index
查詢結果: /Search/Beverages Results
  /Search/ASP.NET Results

我們可以通過在預設的 /[controller]/[action]/[id] 規則之前添加2條自訂的URL路徑映射規則來啟用這些比較好看的查詢結果URL,象下面這樣:

在前2條規則中,我們現在明確地指定了對應 /Search/ URL的控制器和Action參數。我們表明,"/Search" 應該總是由SearchController上的“Index” action來處理。而任何擁有子URL階層的URL (/Search/Foo, /Search/Bar等等 )則總是由SearchController上的 "Results" action 來處理。

上面的第二條直接選取規則表明,在 /Search/ 首碼之後的任何字元應該當作名為"[query]"的參數來處理,這個參數將作為方法參數來傳入SearchController上的Results action方法中:

最有可能的,我們還會對查詢結果啟用分頁(我們每次只顯示10個查詢結果)顯示。我們可以通過查詢字串值的方法來實現(譬如, /Search/Beverages?page=2),或者我們也可以把頁號嵌在URL中(譬如/Search/Beverages/2)。要支援後面這個做法的話,我們需要做的是,給我們的第二條直接選取規則再加一個額外的可省參數:

注意,上面的新URL規則現在匹配的是“Search/[query]/[page]"。我們還將預設的頁號配置為1,萬一頁號沒有包含在URL之中的話(這是通過作為“Defaults”屬性值的匿名型別傳入的)。

然後我們可以把我們的SearchController.Results action方法更新為接受頁號參數作為一個方法參數:

 

這樣,我們就有比較好看的查詢URL了(剩下的就是實現這個查詢演算法,我將把它作為練習留給讀者來完成 <g>)。

直接選取規則的驗證先決條件

就象我在這個貼子前面提到的,Route類有個Validation屬性,允許你添加為使直接選取規則匹配,必須為真的驗證先決條件規則(除了URL過濾外)。ASP.NET MVC架構允許你使用Regex來驗證URL中的參數值,也允許你對HTTP Headers進行評估(根據HTTP動詞的不同進行不同的URL直接選取)。

下面是一個我們可以用到象 /Products/Detail/43 這樣的URL身上的自訂的驗證規則,它指定了其中的ID參數必須是數字(不允許字串),而且它的長度必須在1到8之間:

如果我們往應用中傳入象 /Products/Detail/12 這樣的URL,上面的直接選取規則是合法的,但如果傳入 /Products/Detail/abc 或 /Products/Detail/23232323232,它就不會匹配。

從直接選取系統構建輸出的URL

在本文的前面,我說過ASP.NET MVC架構中的URL直接選取系統負責兩件事情:

  1. 把進來的URL映射到處理的Controllers/Actions上
  2. 幫著構建可以在以後用來回調Controllers/Actions的輸出到用戶端的URL(例如,表單提交, <a href="">連結, 和 AJAX 調用等等)

URL直接選取系統有不少輔助方法和類,方便你在運行時動態查看和構建URL(你也可以直接對RouteTable的Route集合進行操作來查看URL)。

Html.ActionLink

在本部落格系列的第一部分,我簡單地討論了Html.ActionLink()視圖輔助方法。它可以在視圖裡使用,允許你動態地產生 <a href=""> 超連結。比較酷的是,它可以使用MVC直接選取系統裡定義的URL映射規則來產生這些URL。例如,下面2個Html.ActionLink 調用:

automatically pick up the special Search results route rule we configured earlier in this post, and the "href" attribute they generate automatically reflect this: 會自動地使用我們在本貼子前面配置的的特殊查詢結果路徑規則,它們自動產生的href屬性反映了這個情況:

特別地,注意上面,Html.ActionLink的第二個調用自動地把page參數映射成URL的一部分(也注意,第一個調用省略了page參數值,因為它知道伺服器端會自動提供預設值)。

Url.Action

除了使用Html.ActionLink外,ASP.NET MVC還有個Url.Action()視圖輔助方法。該方法產生原生的字串URL,然後你可以任何方式來使用它們。例如,下面的程式碼片段:

會使用URL直接選取系統返回下面這個原生的URL(而不是封裝在 <a href=""> 元素裡):

Controller.RedirectToAction

ASP.NET MVC還提供了Controller.RedirectToAction()輔助方法,你可以在控制器裡使用來進行轉向操作(URL是使用URL直接選取系統計算出來的)。

例如,當在控制器裡調用下面代碼時:

 

在內部,它會產生一個對Response.Redirect("/Search/Beverages")的調用。

DRY (別重複自己)

上述所有的輔助方法的好處在於它們允許我們避免在我們的控制器和視圖邏輯中硬寫URL。如果在後來我們決定改變查詢URL路徑映射規則,從 "/Search/[query]/[page]" 改回到 "/Search/Results/[query]/[page]" 或者 "/Search/Results?query=[query]&page=[page]" ,我們只要在一個地方(我們的路徑註冊代碼中)做編輯,就可以輕鬆搞定。我們不需要改動視圖或控制器中的任何代碼,就可以撿起新的URL(這就堅持了“DRY原則”)。

使用Lambda運算式從直接選取系統構建輸出的URL

前面的URL輔助方法例子使用了VS 2008中VB和C#現在支援的新的匿名型別。在上面的例子中,我們使用了匿名型別來有效地傳入一串名稱/數值對,用以協助映射URL(你可以把這想像為產生字典的一個比較乾淨的方式)。

除了使用匿名型別以動態方式傳遞參數外, ASP.NET MVC架構還支援使用強型別機制建立action路徑的能力,這些強型別機製為URL輔助方法提供了編譯時間檢查和intellisense。這是通過使用泛型和新的VB和C#對Lambda運算式的支援來實現的。

例如,下面這個匿名型別 ActionLink 調用:

也可以寫成:

除了寫起來簡短外,這第二個選項還有型別安全的好處,這意味著你得到對錶達式的編譯時間檢查以及Visual Studio的代碼intellisense(你還可以使用重構工具對它進行重構):

注意上面,我們是如何使用intellisense挑選出我們想用的SearchController的Action方法的,以及參數是強型別的。產生的URL都是由ASP.NET MVC URL路經選擇系統驅動的。

你也許在想,這到底是怎麼回事呢?如果你還記得,8個月前,我在部落格裡討論Lambda運算式時,我談到了Lambda運算式既可以編譯出成代碼代理(delegate),也可以編譯成運算式樹狀架構對象,然後在運行時可以用來分析Lambda運算式。對於 Html.ActionLink<T> 輔助方法,我們使用這個運算式樹狀架構選項,然後在運行時分析對應的lambda,查出它調用的action方法以及相關的參數類型,在運算式中指定的名稱和值等。然後我們可以在MVC URL直接選取系統中使用這些資訊, 返回合適的URL和相關聯的HTML。

重要注意事項: 當使用這Lambda運算式方法時,我們實際上從不運行對應的Controller action方法。例如,下面的代碼並不調用我們的SearchController中"Results" action方法:

實際上,它只是返回這個HTML超連結:

如果這個超連結被使用者點擊的話,它會向伺服器發回一個請求,該請求會調用SearchController的Results action方法。

單元測試路徑

ASP.NET MVC架構的一個核心設計原則是促進很好的測試支援。跟MVC架構的其他部分一樣,你可以輕鬆地單元測試路徑和路徑匹配規則。MVC直接選取系統可以獨立於ASP.NET產生執行個體和運行,這意味著你可以在任何單元測試庫裡裝載和單元測試路徑模式(而不用啟動web伺服器),可以使用任何單元測試架構(NUnit, MBUnit, MSTest等等)。

雖然你可以在你的單元測試中直接單元測試一個ASP.NET MVC應用的全域RouteTable映射集合,但一般來說,讓單元測試改變或者依賴於一個全域的狀態不是一個很好的主意。一個你可以使用的較好的模式是,把你的路徑註冊邏輯放在一個象下面這樣的RegisterRoutes()輔助方法中,對作為參數傳入的RouteCollection進行操作(註:我們也許會把這個模式在下個預覽更新中做成預設的VS模板模式):

然後,你可以編寫單元測試,建立自己的RouteCollection執行個體,調用Application的RegisterRoutes輔助方法,在其中註冊應用的直接選取規則。然後,你可以嚮應用發出類比請求,核實這些請求確有註冊了的正確的控制器和action方法,而不用擔心任何副作用:

結語

希望這個貼子提供了關於ASP.NET MVC直接選取架構工作原理的一些細節,以及你如何可以使用它來定製發布在你的ASP.NET MVC應用中的URL的結構和布局。

在預設情形下,在你建立一個新的ASP.NET MVC Web應用時,它會預先定義一個你可以使用的預設的 /[controller]/[action]/[id] 直接選取規則,而不必手工配置或啟用什麼。這應該允許你不用註冊你自己的自訂直接選取規則,就可以建造許多應用。但希望上面的內容示範了,如果你想對你自己的URL格式做自訂結構的話,做起來並不難, MVC架構對此提供了許多的功能和靈活性。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.