2010-05-22 88 views
6

我想達到一些非常類似於this question,有一些增強。如何使用ASP.NET路由路由樹形結構的URL?

有一個ASP.NET MVC Web應用程序。

我有一個實體樹。
例如,一個Page類具有名爲Children的屬性,類型爲IList<Page>。 (Page類的一個實例對應於數據庫中的一行。)

請注意,網站的所有者可以隨時添加新頁面或刪除現有頁面,並且URL也應反映這些更改。

我想爲數據庫中的每個Page分配一個唯一的URL。
我用一個名爲PageController的控制器處理Page對象。

網址示例:

http://mysite.com/Page1/ 
http://mysite.com/Page1/SubPage/ 
http://mysite.com/Page/ChildPage/GrandChildPage/ 

你得到的圖片。
因此,我希望每一個Page對象都有自己的URL,它的父母的URL和自己的名字相同。
除此之外,我還希望能夠將單個Page映射到/(根)URL。

我想應用這些規則:

  1. 如果URL可以與任何其他途徑進行處理,或文件在指定的URL文件系統的存在,讓默認的URL映射發生
  2. 如果URL可以通過虛擬路徑提供來處理,讓這種處理它
  3. 如果沒有,對方的URL映射到PageController

我也發現this questionalso this onethis one,但他們沒有太大的幫助,因爲他們沒有提供關於我的前兩點的解釋。

我看到以下可能soutions:

  • 地圖的路線爲每個頁面invidually。
    這需要我在應用程序啓動時遍歷整個樹,並將完全匹配路線添加到路由表的末尾。
  • 我可以添加一條路線{*path}並編寫一個處理它的自定義IRouteHandler,但是我看不到如何處理前兩條規則,因爲此處理程序將處理所有內容。

到目前爲止,第一個解決方案似乎是正確的,因爲它也是最簡單的。但是,即使在這種情況下,我也不確定我怎麼能讓PageController來處理這些請求。

我真的很感激你對此的看法。

預先感謝您!

編輯: 我現在有時間檢查我收到的每個答案的每個方面。我接受了Neal的回答,因爲他是提供有關事情如何運作的最佳解釋的人。我也提出了所有其他答案,因爲它們提供了很好的想法。

回答

3

路由按照它們添加到集合的順序進行處理。您可以在現有路線之後添加自定義路線,以確保它是最後一個有機會處理請求的路線。這將允許您爲其之前的現有文件(虛擬或其他文件)添加路由並因此符合標準1和2.

默認情況下,MVC路由將在應用存儲在路由集合中的任何路由之前路由到現有文件;見http://msdn.microsoft.com/en-us/library/system.web.routing.routecollection.routeexistingfiles.aspx。 (帽子給保羅 - 見評論)。

要將請求路由到您的頁面控制器,只需創建檢查虛擬路徑的自定義路由,並且它與數據庫中頁面的模式匹配返回RouteData。使用從虛擬路徑中提取的適當值設置您的RouteData(例如,將Path鍵設置爲/ Parent/Child/Grandchild),將控制器鍵設置爲您的頁面控制器名稱(例如Page)並將動作設置爲操作名稱你想要執行(例如顯示)。應使用MvcRouteHandler創建RouteData(不確定這是否是正確的類名)。

要確保網址到你的數據庫驅動的網頁都正確返回,覆蓋的RouteBaseGetVirtualPath(RequestContext, RouteValueDictionary)方法和用途傳遞,以確定這是否是一個數據庫驅動的網頁的路徑值,如果它是創建所需的虛擬路徑數據(否則返回null)。

對於具有壓倒一切的GetRouteDataGetVirtualPath幫助,看看System.Web.Routing.RouteBaseSystem.Web.Routing.Route反射源代碼;之後谷歌是你的朋友。

路由被反向使用以確定給定控制器,操作和任何其他路由值的url。您應該能夠利用它在請求的上下文內構建頁面的url。

+0

爲什麼以及如何添加現有文件的路線?另外,我將如何構建樹的路線並將其引導至相應的控制器? – Venemo 2010-05-31 17:03:01

+1

你不需要;默認情況下,路由系統將檢查現有文件,並傾向於嘗試查找匹配的控制器和操作。 – Paul 2010-06-05 19:44:52

+0

@保羅 - 謝謝。這是否也適用於由虛擬路徑提供者提供的文件? – Venemo 2010-06-08 18:42:46

1

一個不同的想法是使用T4(文本模板轉換工具包)來讀取您的孩子一次並生成Global.asax文件的內容。

編輯:Bascially與T4你可以自動生成文本文件。例如,你可以用T4引擎讀取集合併爲你生成這些插入語句,而不是手動複製某些巨大集合的項目並將它們粘貼到一個文本文件(如INSERT INTO [MyTable] (Text) VALUES (@ItemText))中。它是靜態的,不適用於運行時。

我覺得很好的介紹可以從Pro Entity Framework 4.0這本書中找到。

但是,如果你說你需要動態地做到這一點,這可能不是你的工具。

+0

如果您能提供更多的細節,我會很高興,我從來沒有聽說過T4。 順便說一句,頁面可以在運行時更改。該網站的管理員可以根據需要添加/刪除/更改它們。 – Venemo 2010-05-23 13:49:38

+0

感謝您指出它可能不是我的工具。事實上,集合在運行時發生變化。無論如何,謝謝你花時間回答我。 :) – Venemo 2010-05-24 09:25:23

1

當您保存頁面時,您會知道您的頁面結構。因此,您可以爲每個頁面生成URL並將其保存到數據庫記錄中。然後,您可以使用{*path}規則並在數據庫中查找完全匹配。此規則應該在規則定義中最後一個,以便您可以匹配其他路由。

例如,您的Page1沒有父頁面,它的網址是Page1。你的SubPage知道它的父母,所以它可以加密url Page1/SubPage

+0

rarouš - 謝謝你的回答!這是一個好的ida,但是不會'{* path}'也抓住真實的(和虛擬的)文件名? – Venemo 2010-06-07 10:21:05

1

你可以使用"Page/{*path}"模式。然後,您可以通過將字符串拆分爲'/'來分解路徑並對其進行分解,或者可以使用Rarouš關於在數據庫中存儲[生成的]路徑的建議並執行直接查找。

如果您使用Rarouš的方法,那麼當父路徑發生更改時,您必須更新表中所有子項的路徑條目。這可以通過單個更新查詢來完成。

我假設您正在映射您希望用於配置文件或表項中某處的主頁的頁面。您可以讓主頁控制器執行查找並返回主頁視圖的內容進行渲染(您可以使用共享視圖,局部視圖或調用頁面控制器,以避免重複行爲),或者你可以讓它重定向到該頁面。

使用這種技術,您可以擁有一個頁面控制器和視圖,以相同的方式處理所有這些頁面。您的其他需求似乎由MVC框架自動處理。

你的路徑應該是這樣的:

http://mysite.com/Page/Page1/ 
http://mysite.com/Page/Page1/SubPage/ 
http://mysite.com/Page/Page/ChildPage/GrandChildPage/ 

當然你也可以使用除「頁面」以外的前綴。

+0

@Andre - 謝謝你的回答!這是迄今爲止最好的主意。 :) – Venemo 2010-06-07 10:19:32

+0

您可以從最具體到最不具體的特定路線條目並刪除前綴,使用RouteTester(http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx)檢查您的路線。 – 2010-06-07 10:23:43

+0

http://stephenwalther.com/blog/archive/2008/08/03/asp-net-mvc-tip-29-build-a-controller-to-debug-your-custom-routes.aspx – 2010-06-07 10:39:09