加入 MVC SiteMap provider 到專案當中

MvcSiteMapProvider
之前開發網站的時候沒有用到類似的工具,或著是用自己定義的元件來做類似的事情,後來在同事推薦下加入到開發當中。

  • 加入 MvcSiteMapProvider 到專案
  • 定義 SiteMap 階層
  • 利用 SiteMap 物件產生選單列表
  • 自行定義 Display Template
  • 搭配角色權限使用 SiteMap
  • 動態產生 SiteMapNode
  • 動態切換多個 SiteMapProvider

加入 MvcSiteMapProvider 到專案

  • 利用 Nuget >  MvcSitemapProvider
  • Nuget  的 Package Manager Console PM> Install-Package MvcSitemapProvider

一開始可以看到加入的檔案中 Mvc.sitemap 就是定義 sitemap 的檔案,檔案格式是用 XML ,預設的部分有加入 Home 跟 About 的部分。

<mvcSiteMapNode title="Home" controller="Home" action="Index">
<mvcSiteMapNode title="About" controller="Home" action="About"/>
</mvcSiteMapNode>

除了 Mvc.sitemap 也會在 Views/Shared/DisplayTemplates 加入配合產生 Ment 的 Display Templates 。

定義 SiteMap 階層

從預設的 .sitemap 檔案就可以看出基本的架構,利用 XML 階層的概念配合整個網站的架構。一開始加入 SiteMap 的另一個好處是會逼得自己了解整個網站的邏輯,遇到規劃上的問題也容易發現。在這邊我直接用 ASP.NET MVC 4 Beta 的預設專案 Template 做 SiteMap。

<mvcSiteMapNode title="首頁" controller="Home" action="Index">
<mvcSiteMapNode title="關於 MVC 4" controller="Home" action="About"/>
<mvcSiteMapNode title="會員登入" controller="Account" action="Login"/>
<mvcSiteMapNode title="註冊會員" controller="Account" action="Register"/>
<mvcSiteMapNode title="變更密碼" controller="Account" action="ChangePassword"/>
</mvcSiteMapNode>

利用 SiteMap 物件產生選單列表

定義完 SiteMap 之後,可以用來幫助我們產生一些頁面上的元件。比較常用的就是 Header 的 Menu 或是 SiteMap Path 之類的。

Html.MvcSiteMap().SiteMap()

我替換掉 Layout.cshtml 下的 menu 並且換上 MvcSiteMapProvider 的 Helper。

<nav>
@Html.MvcSiteMap().SiteMap()
</nav>

產生出來的 Html

<ul id="menu">
<li><a href="/">首頁</a> </li>
<li><a href="/Home/About">關於 MVC 4</a> </li>
<li><a href="/Account/Login">會員登入</a> </li>
<li><a href="/Account/Register">註冊會員</a> </li>
<li><a href="/Account/ChangePassword">變更密碼</a> </li>
</ul>

image
這邊預設的 menu 比較簡單,如果想要能有華麗一點的多階層 menu 就是要修改 Template 配合 javascript css。

自行定義 Display Template

在評估的時候,大家都會蠻害怕如果 Helper 的格式不能變動怎麼辦,或是說被元件綁死,但是其實完全都是可以自行定義的。之前有提到一開始加入的 Display Templates 資料夾裡面就是產出的格式,MvcSiteMapProvider 做的事只是利用 XML 裡面的定義幫忙產生物件的階層。Html 顯示的邏輯都是自訂的。我比較習慣 Razor 的寫法,所以移除掉了 ascx 的部分,但是其實做的事情都是一樣的。

  • MenuHelperModel.cshtml > @Html.MvcSiteMap().Menu()
  • SiteMapHelperModel.cshtml > @Html.MvcSiteMap().SiteMap()
  • SiteMapNodeModel.cshtml > 單一 Node 顯示方式
  • SiteMapNodeModelList.cshtml > List<Node> 顯示方式
  • SiteMapPathHelperModel.cshtml > @Html.MvcSiteMap().SiteMapPath()
  • SiteMapTitleHelperModel.cshtml > @Html.MvcSiteMap().SiteMapTitle()

這只是預設的設定,其實只要在使用方法的時候多加入參數就可以。

@Html.MvcSiteMap().Menu("想要用的template.cshtml")

搭配角色權限使用 SiteMap

在設定 SiteMapNode 的時候也有一個屬性 Roles 可以做結合

<mvcSiteMapNode title="系統管理" roles="SystemAdmin,Supervisor"
area="Admin" controller="Home" action="Index">
<mvcSiteMapNode title="會員管理" roles="SystemAdmin,Supervisor"
area="Admin" controller="Account" action="Index"/>
</mvcSiteMapNode>

這邊的 roles 就是整合的 .net 的 Forms Authentication 的 Roles 屬性,當沒有權限頁面就不會出現在 SiteMap 裡面。這邊只是針對 SiteMap 部份顯示與否,如果直接存取 action 還是可以進入。不過我們可以配合這個邏輯來自訂 ActionFilter。

public class RolesAuthenticationAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (SiteMap.CurrentNode == null)
{
throw new UnauthorizedAccessException();
}
}
}

只要將 ActionFilterAttribute 套用到 BaseController 或是只想針對個別套用也可以,這樣在權限控管上就可以由 sitemap 來做統一的設定。

自訂角色可以參考 ASP.NET 自訂角色的方式(不用實做 Role Provider)

動態產生 SiteMapNode

如果說需要動態產生 SiteMap 節點,像是 Blogger 這樣,Menu 是根據資料庫或其他來源來產生,那就需要動態來產生。

<mvcSiteMapNode title="" controller="Page" action="Index"
dynamicNodeProvider="MasonPrint.MVC4.Beta.Utilities.DynamicNodeProviders.PageDynamicNodeProvider
, MasonPrint.MVC4.Beta"/>

利用設定 DynamicNodeProvider 來做到動態節點。

public class PageDynamicNodeProvider : DynamicNodeProviderBase
{
public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
{
DatabaseEntities1 db = new DatabaseEntities1();
var returnValue = new List<DynamicNode>();
//根據資料來源建立節點
foreach (var item in db.Pages.ToList())
{
DynamicNode node = new DynamicNode();
node.Title = item.Title;
node.ParentKey = "Page";
node.Key = "Page_" + item.Id;
node.RouteValues.Add("id", item.Id);

returnValue.Add(node);
}

// Return
return returnValue;
}
}

動態切換多個 SiteMapProvider

如果需要動態切換不同的 SiteMapProvider

SiteMap.Provider.ParentProvider = SiteMap.Providers["MvcSiteMapProvider1"];

需要注意事項

  1. .Sitemap 設定中 Key 必須是唯一值
  2. 如果 Action 不存在則 SiteMap 物件中節點不會產生
  3. SiteMap 物件建立之後會暫存在 Server 端

本篇專案下載


ASP.NET MVC SiteMap provider

Documenting [Authorize] attribute usage, producing a report

NuGet Package Manager ★★★★★ – The AppStore in Visual Studio

概略解釋 Forms Authentication 的運作

ASP.NET 自訂角色的方式(不用實做 Role Provider)