在Asp.net中,SiteMap用于站点导航,可以与Menu等控件一起使用实现网站的菜单和权限管理。但是SiteMap提供的方法都是只读的,无法再运行时修改(菜单)导航文件,需要手动修改配置web.sitemap文件,某些情况下,这样做很不方便。
本文即针对这个问题,模仿SiteMap,实现一个可写的菜单和权限控制组件,称之为 MenuMap。
MenuMap高仿SiteMap,使用方法类似,下面进入主题。
1. 模仿Web.sitemap定义配置文件,文件名和位置自定义,比如叫menu.xml。注意在部署是保证network services 账号对该文件有读和写的权限。
格式如下:
<?xml version="1.0" encoding="utf-8"?> <menuMap> <menuMapNode url="" title="管理系统" description="" icon="" roles=""> <menuMapNode url="" title="业务管理" description="" icon="" roles=""> <menuMapNode url="Business/b1.aspx" title="待办事宜" description="" icon="" roles="超级管理员,总部经理,地区经理" /> <menuMapNode url="Business/b2.aspx" title="积分管理" description="" icon="" roles="超级管理员,总部经理,地区经理" /> <menuMapNode url="Business/b4.aspx" title="工作流" description="" icon="" roles="超级管理员,总部经理" /> <menuMapNode url="Business/b5.aspx" title="统计报表" description="" icon="" roles="超级管理员,总部经理" /> </menuMapNode> <menuMapNode url="" title="系统管理" description="" icon="" roles="系统管理员,超级管理员"> <menuMapNode url="SysAdmin/UserMgr.aspx" title="用户管理" description="" icon="" roles="超级管理员,系统管理员" /> <menuMapNode url="SysAdmin/RolesAndMenu.aspx" title="权限设置" description="" icon="" roles="超级管理员" /> <menuMapNode url="SysAdmin/AreaMgr.aspx" title="区域信息" description="" icon="" roles="超级管理员" /> <menuMapNode url="SysAdmin/ClearCache.aspx" title="清理缓存" description="" icon="" roles="超级管理员" /> </menuMapNode> </menuMapNode> </menuMap>
基本上同sitemap文件,这里menuMapNode, 多一个属性"icon", 用于给菜单添加图标
2. 实现MenuMapNode类,直接上代码:
1 using System.Collections.Generic; 2 using System.Linq; 3 using System.Text; 4 5 namespace NorthRiver 6 { 7 public class MenuMapNode 8 { 9 public string Title { get; set; } 10 public string Url { get; set; } 11 public string Description { get; set; } 12 public string Roles { get; set; } 13 public string IconUrl { get; set; } 14 public MenuMapNode Parent { get; set; } 15 public IList<MenuMapNode> ChildNodes { get; set; } 16 17 public bool HasChildren 18 { 19 get 20 { 21 if (ChildNodes != null && ChildNodes.Count > 0) 22 return true; 23 24 return false; 25 } 26 } 27 28 public override string ToString() 29 { 30 return string.Format("<menuMapNode url="{0}" title="{1}" description="{2}" icon="{3}" roles="{4}" />", Url, Title, Description,IconUrl, Roles); 31 } 32 33 public void RemoveRole(string roleName) 34 { 35 if (string.IsNullOrEmpty(roleName)) 36 return; 37 38 if (HasChildren) 39 { 40 foreach (MenuMapNode child in ChildNodes) 41 { 42 child.RemoveRole(roleName); 43 } 44 } 45 if (!string.IsNullOrEmpty(Roles)) 46 { 47 string[] roleArray = Roles.Split(','); 48 StringBuilder sb = new StringBuilder(); 49 for (int i = 0; i < roleArray.Length; i++) 50 { 51 if (roleArray[i] != roleName) 52 { 53 if (i > 0 && sb.Length > 0) 54 sb.Append(','); 55 sb.Append(roleArray[i]); 56 } 57 } 58 Roles = sb.ToString(); 59 } 60 } 61 62 public bool HasRole(string role) 63 { 64 if (string.IsNullOrEmpty(Roles)) 65 return true; 66 67 string[] roleArray = Roles.Split(','); 68 return roleArray.Contains(role); 69 } 70 71 public bool HasRoles(string[] roleArray) 72 { 73 if (string.IsNullOrEmpty(Roles)) 74 return true; 75 76 if (roleArray == null) 77 return false; 78 79 foreach (string role in roleArray) 80 { 81 if (HasRole(role)) 82 return true; 83 } 84 85 return false; 86 } 87 //是否有角色 88 public bool HasRoles(string roles) 89 { 90 if (string.IsNullOrEmpty(roles)) 91 return false; 92 93 string[] roleArray = roles.Split(','); 94 return HasRoles(roleArray); 95 } 96 //添加节点 97 public void AddNode(MenuMapNode childNode) 98 { 99 if (childNode != null) 100 { 101 if (this.ChildNodes == null) 102 this.ChildNodes = new List<MenuMapNode>(); 103 104 childNode.Parent = this; 105 this.ChildNodes.Add(childNode); 106 } 107 } 108 109 public bool TryMatchUrl(string url, ref MenuMapNode matchNode) 110 { 111 if (this.Url == url) 112 { 113 matchNode = this; 114 return true; 115 } 116 117 if (this.HasChildren) 118 { 119 foreach (MenuMapNode child in ChildNodes) 120 { 121 if (child.TryMatchUrl(url, ref matchNode)) 122 return true; 123 } 124 } 125 126 return false; 127 } 128 } 129 }
3.实现MenuMap类,直接上代码:
using System; using System.Collections.Generic; using System.Web; using System.Configuration; using System.Xml; using System.Text; namespace NorthRiver { public class MenuMap { private static string _menuFileName = "MenuMapFile"; private static string _rootCacheKey = "MenuMapCache"; public static MenuMapNode RootNode { get { return LoadMenu(); } } public static MenuMapNode LoadMenu() { object objRoot = GetCache(_rootCacheKey); if (objRoot == null) { string filename = ConfigurationManager.AppSettings[_menuFileName]; filename = HttpContext.Current.Server.MapPath(filename); XmlReaderSettings setting = new XmlReaderSettings(); setting.IgnoreComments = true; using (XmlReader xmlReader = XmlReader.Create(filename, setting)) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(xmlReader); XmlNode xmlRoot = xmlDoc.DocumentElement.FirstChild; MenuMapNode menuRoot = new MenuMapNode(); menuRoot.Title = xmlRoot.Attributes["title"] == null ? "" : xmlRoot.Attributes["title"].Value; menuRoot.Url = xmlRoot.Attributes["url"] == null ? "" : xmlRoot.Attributes["url"].Value; menuRoot.Description = xmlRoot.Attributes["description"] == null ? "" : xmlRoot.Attributes["description"].Value; menuRoot.IconUrl = xmlRoot.Attributes["icon"] == null ? "" : xmlRoot.Attributes["icon"].Value; menuRoot.Roles = xmlRoot.Attributes["roles"] == null ? "" : xmlRoot.Attributes["roles"].Value; menuRoot.ChildNodes = new List<MenuMapNode>(); if (xmlRoot.HasChildNodes) { foreach (XmlNode xmlNode in xmlRoot.ChildNodes) CreateMenuNode(xmlNode, menuRoot); } SetCache(_rootCacheKey, menuRoot); return menuRoot; } } return (MenuMapNode)objRoot; } private static void CreateMenuNode(XmlNode xmlNode, MenuMapNode parentNode) { MenuMapNode menuNode = new MenuMapNode(); menuNode.Title = xmlNode.Attributes["title"] == null ? "" : xmlNode.Attributes["title"].Value; menuNode.Url = xmlNode.Attributes["url"] == null ? "" : xmlNode.Attributes["url"].Value; menuNode.Description = xmlNode.Attributes["description"] == null ? "" : xmlNode.Attributes["description"].Value; menuNode.IconUrl = xmlNode.Attributes["icon"] == null ? "" : xmlNode.Attributes["icon"].Value; menuNode.Roles = xmlNode.Attributes["roles"] == null ? "" : xmlNode.Attributes["roles"].Value; menuNode.Parent = parentNode; menuNode.ChildNodes = new List<MenuMapNode>(); if (xmlNode.HasChildNodes) { foreach (XmlNode node in xmlNode.ChildNodes) CreateMenuNode(node, menuNode); } parentNode.ChildNodes.Add(menuNode); } public static void Save() { XmlWriterSettings settings = new XmlWriterSettings(); //要求缩进 settings.Indent = true; //注意如果不设置encoding默认将输出utf-16 //注意这儿不能直接用Encoding.UTF8如果用Encoding.UTF8将在输出文本的最前面添加4个字节的非xml内容 settings.Encoding = new UTF8Encoding(false); //设置换行符 settings.NewLineChars = Environment.NewLine; string filename = ConfigurationManager.AppSettings[_menuFileName]; filename = HttpContext.Current.Server.MapPath(filename); MenuMapNode rootNode = RootNode; lock (rootNode) { using (XmlWriter xmlWriter = XmlWriter.Create(filename, settings)) { //写xml文件开始<?xml version="1.0" encoding="utf-8" ?> xmlWriter.WriteStartDocument(); //写根节点 xmlWriter.WriteStartElement("menuMap"); //递归写SiteMapNode WriteMenuMapNode(xmlWriter, rootNode); xmlWriter.WriteEndElement(); XmlDocument doc = new XmlDocument(); doc.Save(xmlWriter); } } } private static void WriteMenuMapNode(XmlWriter xmlWriter, MenuMapNode node) { xmlWriter.WriteStartElement("menuMapNode"); xmlWriter.WriteAttributeString("url", node.Url); xmlWriter.WriteAttributeString("title", node.Title); xmlWriter.WriteAttributeString("description", node.Description); xmlWriter.WriteAttributeString("icon", node.IconUrl); xmlWriter.WriteAttributeString("roles", node.Roles); if (node.HasChildren) { foreach (MenuMapNode child in node.ChildNodes) WriteMenuMapNode(xmlWriter, child); } xmlWriter.WriteEndElement(); } public static object GetCache(string CacheKey) { System.Web.Caching.Cache objCache = HttpRuntime.Cache; return objCache[CacheKey]; } public static void SetCache(string CacheKey, object objObject) { System.Web.Caching.Cache objCache = HttpRuntime.Cache; objCache.Insert(CacheKey, objObject); } public static void RemoveCache() { System.Web.Caching.Cache objCache = HttpRuntime.Cache; objCache.Remove(_rootCacheKey); } } }
4.在web.config中配置菜单文件路径,如:
<appSettings> <add key="MenuMapFile" value="~\menu.xml" /> </appSettings>
5.使用举例:
private void CreateMenu() { IList<MenuMapNode> lv1Nodes = MenuMap.RootNode.ChildNodes; foreach (MenuMapNode node in lv1Nodes) { MenuPanel menuPanel = CreateMenuPanel(node); if (CheckPermissions(node)) this.LeftPanel.Items.Add(menuPanel); } } private MenuPanel CreateMenuPanel(MenuMapNode node) { MenuPanel menuPanel = new MenuPanel(); menuPanel.Title = node.Title; menuPanel.Border = false; if (node.HasChildren) { foreach (MenuMapNode child in node.ChildNodes) { Ext.Net.MenuItem item = new Ext.Net.MenuItem(); item.Text = child.Title; item.Icon = Icon.ApplicationHome; item.Listeners.Click.Handler = string.Format("e.stopEvent(); loadPage('{0}', '{1}','{2}');", child.Url, "id" + child.Url.GetHashCode(), child.Title); // object o = child.Roles; if (CheckPermissions(child)) menuPanel.Menu.Items.Add(item); } } return menuPanel; } private bool CheckPermissions(MenuMapNode node) { //未做标记的菜单,对所有角色可见 if (string.IsNullOrEmpty(node.Roles)) return true; if (Session["user_roles"] == null) return false; return node.HasRoles((string[])Session["user_roles"]); }
补充,MenuMap是可写的,示例:
if (MenuMap.RootNode.HasChildren) MenuMap.RootNode.ChildNodes.Clear(); foreach (SubmittedNode sNode in e.RootNode.Children) { MenuMapNode mNode = CreateMenuMapNode(sNode); MenuMap.RootNode.AddNode(mNode); } MenuMap.Save();