ASP.NET站点导航(五)
理解并擴展 ASP.NET 2.0 中的站點導航系統 http://msdn.microsoft.com/zh-cn/library/aa479338.aspx
發布日期 : 2006-3-15 | 更新日期 : 2006-3-15David Gristwood
Developer & Platform Group, Microsoft
適用于:
Microsoft ASP.NET 2.0 (Beta 2)
摘要:ASP.NET 2.0 站點導航系統構建于一個功能強大、靈活的體系結構之上,設計這樣的體系結構是為了使其具有可擴展性。本文探究站點提供程序的體系結構并提供一個示例提供程序,該提供程序將文件系統公開為站點導航的數據源,從而替代了標準的 Web.sitemap XML 文件。
請從此處下載示例。
本頁內容
簡介
理解 ASP.NET 2.0 中的導航系統
導航控件
使用導航系統
實現您自己的站點地圖提供程序
一個文件夾站點地圖提供程序
小結
簡介
大多數 web 站點采用可視化導航的某種形式來幫助用戶輕松地瀏覽站點,以及查找他們所需的信息和 Web 頁。盡管不同站點之間的感觀效果千差萬別,但是通常會使用相同的基本元素 — 以導航欄或菜單列表的形式使用戶定位到 web 站點的特定位置。
ASP.NET 1.x 提供的針對站點導航現成的支持很少,導致很多開發人員和 web 設計人員不是構建自己的導航系統,就是購買第三方控件以滿足他們的需求。而 ASP.NET 2.0 對此作出了改進,它引入一個使用可插接式框架的導航系統,該框架能夠公開站點層次結構和插入這個新模型的控件,因此易于構造一個高質量的菜單和導航系統。
本文描述 ASP.NET 2.0 導航系統的工作原理并展示如何對其進行擴展 — 不僅僅是使用簡單的 XML 文件(Visual Studio 2005 中使用的默認機制)。
返回頁首理解 ASP.NET 2.0 中的導航系統
ASP.NET 2.0 導航系統的一個目標是創建一個可以吸引開發人員和 web 站點設計人員的優秀的導航模型,除此之外,它還有一個目標是創建一個提供可擴展性功能的體系結構,該功能能夠靈活地滿足廣泛的需求。該系統基于一個提供程序模型,該模型的使用貫穿于整個 ASP.NET 2.0 框架,由 ASP.NET 2.0 框架提供一個標準的機制用于插入不同的數據源。
ASP.NET 2.0 導航框架可以分解為幾個部分:
-
開發人員在實際 web 頁面上使用的web 導航控件(Menu、TreeView 和 SiteMapPath)。這些控件可以通過自定義改變感觀效果。
-
TreeView 和菜單導航控件綁定的 SiteMapDataSource 控件,在 Web 導航控件和導航信息的底層提供程序之間提供一個抽象層。
-
站點地圖提供程序是可插接式提供程序,它用于公開描述 web 站點布局的實際信息。ASP.NET 提供了一個提供程序 XmlSiteMapProvider,它使用一個具有特定結構的 XML 文件作為其數據存儲。
這種分層的體系結構在底層的站點層次結構和 web 站點上的控件之間制造了更為松散的耦合,提供了更大的靈活性,而且隨著站點的不斷發展,更容易實現體系結構和設計的改動。
以下表格說明提供程序和控件之間的關系。
圖 1. 導航體系結構對于導航系統,數據源描述用戶能夠定位的 web 站點頁的層次結構,以及將這些信息顯示給用戶的方式。它作為一個站點地圖被引用。一個簡單的 web 站點的布局可以是以下形式:
Home Products Product A Product B Product C Latest Offers Contact Us Email Visit us 返回頁首導航控件
在深入研究導航系統的內部工作機制之前,了解開發人員如何與之交互十分重要。最常見的方法是通過 ASP.NET 2.0 中的三個新導航控件。一個站點或頁面上可以存在多個 Web 導航控件 — 如,主菜單控件放置在頁面左側,另一個菜單控件放在頁面頂部,這種情況還是時有發生的,我們能夠以編程方式使一個導航控件控制頁面上另一個導航控件,或使它們各自獨立操作。
導航系統通常與母版頁功能一起使用,母版頁功能也是 ASP.NET 2.0 中提供的新功能。通過將導航控件放置在站點的母版頁上,可以確保整個站點具有統一的感觀效果。但是,導航功能和母版頁功能互不相關且各自獨立。
這三個新 web 控件是:
-
菜單控件 — 它提供一個傳統的導航界面,通常的情況是沿 web 站點的一側或橫跨頂部。它能夠顯示任意數目的嵌套子菜單,而且,當用戶的鼠標停懸在某一項上時,可以顯示任意數目的可選彈出子菜單。
圖 2. 菜單控件 -
TreeView 控件 — 它提供一個垂直的樹狀用戶界面,通過選擇單個節點可以展開和折疊用戶界面。它還提供用于選定某些項的復選框功能。
圖 3. TreeView 控件 -
SiteMapPath 控件 — 它通常作為“breadcrumb”控件進行引用,因為它可以跟蹤用戶在站點層次結構內的位置。它將當前位置顯示為一個路徑,通常是從主頁到當前位置的路徑,因而用戶更容易了解自己所處的位置,并定位回路徑上的其他頁面。
圖 4. SiteMapPath 控件
從體系結構的角度來看,菜單控件和 TreeView 控件有些相似,兩者之間的主要區別是編程以及顯示方式不同,因此,各自擁有獨特的感觀效果。要詳細了解這兩個控件,請參閱 MSDN 中的文章 Introducing the ASP.NET 2.0 TreeView and Menu Controls。值得注意的是,雖然使用菜單控件和 TreeView 控件的最常見情況離不開站點導航,但是它們也能夠用于非導航情況,這有待于用戶自己做選擇。
SiteMapPath (或“breadcrumb”)控件則略為不同。它直接使用 SiteMapProvider,而不是菜單控件和 TreeView 控件使用的 SiteMapDataSource 控件。這說明它是導航系統內部更有針對性的控件,因此支持將其功能擴展到非導航情況的理論依據更少。
返回頁首使用導航系統
理解站點導航工作原理的一種最簡單的方法是,直接在一個應用程序內部訪問它,而不是通過一個 Web 控件來訪問。下列代碼示例說明如何與 SiteMap 對象模型進行交互以顯示一部分層次結構化的站點信息。
<%@ Page Language="C#" %> <script runat="server"> private void Page_Load(object sender, System.EventArgs e) { this.Label1.Text = "Current Page Title : " + SiteMap.CurrentNode.Title; if(SiteMap.CurrentNode.ChildNodes > 0) { this.HyperLink1.NavigateUrl = SiteMap.CurrentNode.ChildNodes[0].Url; this.HyperLink1.Text = SiteMap.CurrentNode.ChildNodes[0].Title; } } </script> <html> <head> </head> <body> <form id="Form1" runat="server"> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label><br /> First Child Node: <asp:HyperLink id="HyperLink1" runat="server">HyperLink</asp:HyperLink> </form> </body> </html>導航系統將站點地圖模型化為一系列節點,稱為 SiteMapNodes,在一個樹狀結構內,每個節點通常表示用戶能夠定位到的 web 站點上的一個頁面(可能有一些節點只是子頁面的占位符,或與之類似,本身沒有 Web 頁)。
SiteMapNode 類具有一些屬性和方法,其中最重要的一些是:
-
Title — 當此節點通過一個 web 控件顯示時將呈現的文本
-
Url — SiteMapNode 在 web 站點內表示的實際頁面的 URL
-
Description — 當鼠標懸停在 HTML 頁面的節點上時顯示的工具提示
SiteMapNode 類還有一些屬性用于保存各個 SiteMapNodes 之間的關聯,這些關聯描述出站點地圖的結構(ChildNodes、NextSibling、PreviousSibling、ParentNode 等等)。
返回頁首實現您自己的站點地圖提供程序
如前所述,ASP.NET 2.0 發行了一個名為 XmlSiteMapProvider 的導航數據存儲提供程序,這是大部分開發人員熟悉的默認提供程序。該提供程序使用一個表示 SiteMapNodes 的 XML 文件中的數據。在使用 Visual Studio 2005 時,如果您在 Web 項目中添加了一個新站點地圖,那么在默認情況下,在項目內將創建一個 Web.sitemap 文件,并按照下列模板格式進行填充。
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" > <siteMapNode url="" title="" description=""> <siteMapNode url="" title="" description="" /> <siteMapNode url="" title="" description="" /> </siteMapNode> </siteMap>您一眼就可以看出,前面提到的核心 SiteMapNodes 屬性(title、url 和 description)就是 Web.sitemap XML 架構中的屬性,而且樹結構(父級、子級等等)是由嵌套的 SiteMapNodes 表示的。
這是處理站點地圖信息的一種簡單且精練的方法,對于許多 Web 設計人員而言,使用起來綽綽有余。然而,ASP.NET 提供程序模型具有擴展性并不意味著您能夠編寫自己的提供程序。創建一個自定義站點地圖提供程序要考慮三個可能的主要原因:
-
站點地圖信息保存在一個非 XML 文件的數據源中,例如數據庫或目錄服務。
-
站點地圖信息以 XML 的形式使用,但是使用的架構與 Web.sitemap 使用的架構不同。
-
需要一個動態的站點地圖結構,該結構需要在運行時構造,或者需要一個安全剪裁不能處理的自定義視圖。
為了實現您自己的站點地圖提供程序,您需要從 System.Web 命名空間的 SiteMapProvider 抽象類中派生一個自定義的提供程序類。雖然 SiteMapProvider 類具有大約二十幾個抽象方法或虛方法,但是在您的自定義站點地圖提供程序中,只有少數方法需要重寫或實現。
如果您的自定義站點地圖提供程序使用的數據存儲與默認的 XmlSiteMapProvider 的 Web.sitemap XML 架構相似,則應該選擇從 StaticSiteMapProvider 類派生,它提供 SiteMapProvider 類的部分實現,并反過來作為實際的 XmlSiteMapProvider 類的基類。
您的類必須至少實現下列方法:
-
FindSiteMapNode — 返回一個與特定 URL 對應的 SiteMapNode。
-
GetChildNodes — 返回一個特定 SiteMapNode 的子節點集合。
-
GetParentNode — 返回一個特定 SiteMapNode 的父節點。
-
GetRootNodeCore — 返回當前提供程序目前管理的所有節點的根節點。
您還應該重寫 SiteMapProvider 的 Initialize 方法,在調用基類 Initialize 之后在該方法中執行您自己的初始化。
如果有必要,您可以選擇重寫與節點相關的一些屬性,從而構造更為復雜的表示站點地圖的模型:
-
SiteMapNode — 返回映射到當前 HTTPContext URL 的節點。
-
RootNode — 返回存儲根部的節點。
-
RootProvider — 返回根部的提供程序,以允許連鎖提供程序。
這些屬性和方法代表了 SiteMapProvider、Menu / TreeView 控件(通過 SiteMapDataSource)和 SiteMapPath 控件之間的協定。在以下兩種情況下會發生這種交互:控件請求填充顯示所需的信息,以及用戶通過站點進行導航以跟蹤站點導航內的當前位置。它是一個事件驅動模型,在交互過程中,導航系統調入提供程序。
如果編寫自己的提供程序,您需要決定 SiteMapNodes 是只讀還是可寫 — 如果是可寫的,您需要考慮線程安全,在適當的情況下鎖定更新代碼,從而保證實現是線程安全的,因為 ASP.NET 通過一個線程池調度請求,因此一個線程池會被多個線程訪問。
一個更細節的問題是有關性能和可伸縮性的。一個 web 站點使用幾個導航控件,或者底層導航數據存儲容量很大的情況并不少見,因此,您需要保證提供程序可以響應,因為這將影響有關設計方面的事宜,包括存儲和檢索數據的最佳算法以及緩存的潛在作用。
安全問題也需要考慮。Web 站點的普遍要求是只允許會員或其他經過身份驗證的用戶查看特定頁面,而 ASP.NET 2.0 的角色管理提供了定義明確的方法,以便根據安全角色限制對 Web 文件的訪問。這一功能通過一個稱為安全剪裁的機制擴展到站點導航系統中。安全剪裁強制在嘗試訪問頁面時也應用 Url 和文件授權。在節點上定義一個 roles 屬性用于擴展對該節點的訪問 — 如果您是在 roles 屬性中定義的角色之一,那么將返回該節點;如果您不是這些角色中的一個,那么將執行 Url 和 File Authoriation 檢查。對于 XmlSiteMapProvider 而言,在一個 SiteMapNode 項中添加一個 roles="managers" 類型的屬性將決定用戶在站點地圖中是否能夠看到該節點。在編寫您自己的站點地圖提供程序時,這通過 IsAccessableToUser 方法進行處理,它根據用戶的角色返回一個布爾值,指示該節點對用戶是否可用。派生的提供程序能夠使用基類 SiteMapProvider 中存在的默認實現。如果要支持安全性,那么站點地圖必須提供某種方法來存儲該信息。
返回頁首一個文件夾站點地圖提供程序
本文中的示例將一個 web 站點內的子目錄公開為實際的站點地圖,這使 web 站點導航能夠直接映射到文件夾結構,這樣的 web 站點結構模型不少見。該示例的重點是說明如何設計和構造一個站點地圖提供程序,在這種情況下,它的目的不是覆蓋行業優點方面所有可能的“邊緣條件”。這些權衡在本文內的適當位置都已強調。
示例提供程序通過 web 站點的子目錄進行遞歸枚舉,檢查每個文件夾(除了 App_* 和 bin 目錄,它們是 ASP.NET 2.0 使用的特殊目錄)是否存在一個 default.aspx 文件。如果存在這個文件,示例提供程序則將其添加到站點地圖中,并使用包含文件夾的名稱作為菜單描述。擴展該示例,使每個目錄不僅支持 default.aspx 項,或者必要時在目錄內的一個文本文件中存儲一個日期工具提示,這實現起來相對比較簡單。
提供程序的核心代碼如下所示。
[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)] public class FolderSiteMapProvider : SiteMapProvider { private SiteMapNode rootNode = null; // root of tree private Hashtable urlHash = null; // hash table based on URL private Hashtable keyHash = null; // hash table based on key string defaultPageName = "Default.aspx"; // only look for this file in each subdirectory string defaultTitle = "Home"; // description of root node // override SiteMapProvider Initialize public override void Initialize(string name, NameValueCollection attributes) { // do base class initialization first base.Initialize(name, attributes); // our custom initialization urlHash = new Hashtable(); keyHash = new Hashtable(); // get web site info string startFolder = HttpRuntime.AppDomainAppPath; string startUri = Uri.EscapeUriString(HttpRuntime.AppDomainAppVirtualPath); // want canonical format of URI // Create root node string key = startFolder; string url = startUri + @"/" + defaultPageName; rootNode = new SiteMapNode(this, key, url, defaultTitle); RootNode.ParentNode = null; urlHash.Add(url, rootNode); keyHash.Add(key, rootNode); // populate entire site EnumerateFolders(rootNode); } // Retrieves a SiteMapNode that represents a page public override SiteMapNode FindSiteMapNode(string rawUrl) { if (urlHash.ContainsKey(rawUrl)) { SiteMapNode n = (SiteMapNode)urlHash[rawUrl]; return n; } else { return null; } } // Retrieves the root node of all the nodes //currently managed by the current provider. // This method must return a non-null node protected override SiteMapNode GetRootNodeCore() { return rootNode; } // Retrieves the child nodes of a specific SiteMapNode public override SiteMapNodeCollection GetChildNodes(SiteMapNode node) { SiteMapNode n = (SiteMapNode)keyHash[node.Key]; // look up our entry, based on key return n.ChildNodes; } // Retrieves the parent node of a specific SiteMapNode. public override SiteMapNode GetParentNode(SiteMapNode node) { SiteMapNode n = (SiteMapNode)keyHash[node.Key]; // look up our entry, based on key return n.ParentNode; } // helper functions . . . // . . . }當提供程序通過其 Initialize 方法進行調用時,示例提供程序生成一個文件夾列表,但是不刷新該列表,因此它不檢查隨時添加的新文件夾,這對一個產品的 web 站點是不合理的。
它不實現安全剪裁,因此所有子目錄都可見,但是,要實現安全剪裁是很簡單的,只要在 FindSiteMapNode、GetChildNodes 和 GetParentNode 方法內添加對基類 IsNodeAccessible 方法的調用就可以了,IsNodeAccessible 方法將自動獲得為 web 站點配置的任何文件授權規則的優點。
提供程序在內部能夠以任意種方法表示底層存儲,但是,因為它通過 SiteMapNode 類與導航系統進行交互,特別是因為目錄層次結構與節點層次結構如此匹配,所以在這種情況下,在內部使用這些方法才有意義。它還維護兩個哈希表,其中一個以節點 URL 為關鍵字,另一個以節點關鍵字(文件夾名)為關鍵字,從而支持快速查詢節點。對于本示例而言,在內存中保存所有這些信息是有意義的,因為它能夠保持簡單的編程模型和非??斓牟樵兯俣?#xff1b;但是,對于一個擁有成千上萬個文件夾的大型站點而言,研究一個不在內存中保存全部信息的機制可能是值得的。
為了保持簡單的代碼,節點沒有以只讀的方式處理。在一個實際的具有行業優點的提供程序中,當應用程序不允許添加或修改站點地圖節點的內部列表時,就將各個節點以及整個集合標識為只讀。
私有的 helper 函數用于構造文件夾層次結構,如以下代碼示例所示。
private void EnumerateFolders(SiteMapNode parentNode) { // create a node collection for this directory SiteMapNodeCollection nodes = new SiteMapNodeCollection(); parentNode.ChildNodes = nodes; // get list of subdirectories within this directory string targetDirectory = parentNode.Key; // we use the key to hold the file directory string[] subdirectoryEntries = Directory.GetDirectories(targetDirectory); foreach (string subdirectory in subdirectoryEntries) { // search for any sub folders in this directory string[] s = subdirectory.Split('\\'); string folder = s[s.Length-1]; string tmp = String.Copy(folder); tmp = tmp.ToLower(); // avoid any case sensitive matching issues // check for App_ and bin directories, and don't add them if (tmp.StartsWith("app_")) continue; if (tmp == "bin") continue; string testFileName = subdirectory + @"\" + defaultPageName; if (File.Exists(testFileName)) { // create new node string key = subdirectory; string url = CreateChildUrl(parentNode.Url, folder); string title = folder; SiteMapNode n = new SiteMapNode(this, key, url, title); n.ParentNode = parentNode; // add it into node collection and table nodes.Add(n); urlHash.Add(url, n); keyHash.Add(key, n); // and enummerate through this folder now EnumerateFolders(n); } } }當提供程序在子目錄中枚舉時,它為每一個合法子目錄創建一個 SiteMapNode 類,并設置它的 key、url 和 title 屬性。它還為每一個子節點填充 ChildNodes 屬性,這個屬性是一個 SiteMapNodeCollection,而 ParentNode 屬性則向上指回父節點。同時,它也將每個節點添加到哈希表中,以便進行快速查詢。
一旦完成這些操作,提供程序需要使用 web.config 文件進行配置。以下項需要添加到 <system.web> 下。
<system.web> . . . <siteMap defaultProvider="SimpleProvider"> <providers> <add name="SimpleProvider" type="Test.SimpleProvider"/> </providers> </siteMap> . . . </system.web> 返回頁首小結
本文的目標是使您了解并正確評價 ASP.NET 2.0 站點導航系統,理解不同元素之間協作的原理。代碼示例說明了如何通過構造您自己的站點地圖提供程序來擴展體系結構,該提供程序使用任意數據源來定義站點層次結構。
關于作者
David Gristwood 在 Microsoft 位于 UK 的開發人員和平臺組工作,他大部分時間用來與客戶和合作伙伴一起工作,幫助他們設計和構建充分利用 Microsoft .NET 平臺的解決方案。他也經常在會議和研討會上發言。
轉載于:https://www.cnblogs.com/min10/archive/2008/11/21/1338475.html
總結
以上是生活随笔為你收集整理的ASP.NET站点导航(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 互联网创新创业大赛优秀范例_大学生互联网
- 下一篇: 七个小矮人