应该为每个页面请求绑定asp菜单项吗



asp菜单控件位于主页面中。它的数据源是一个web.itemap文件。该文件最初将所有项目/页面声明为节点。我写了这个代码,在用户登录后,根据用户权限从菜单中删除项目

protected void MyMenu_MenuItemDataBound(object sender, MenuEventArgs e) 
{
if(Session["MenuLoaded"]==null)
{
SiteMapNode node = (SiteMapNode)e.Item.DataItem;
bool deleteItem = true;
if(lstRoles.Count==0)
lstRoles = (List<tblDetail>)Session["sRoles"];
if(!string.IsNullOrEmpty(node.Description))
{
foreach(var item in lstRoles)
{
if(Convert.ToInt32(node.Description)==item.FormId)
{
deleteItem = false;
break;
}
}
if(deleteItem)
{
if(e.Item.Parent !=null)
{
MenuItem mItem = e.Item.Parent;
mItem.ChildItems.Remove(e.Item);
if(mItem.ChildItems.Count==0)
{
if(mItem.Parent !=null)
{
MenuItem Item = mItem.Parent;
Item.ChildItems.Remove(mItem);
}
else
{
Menu menu = (Menu)sender;
menu.Items.Remove(mItem);
}
}
else
{
Menu menu = (Menu)sender;
menu.Items.Remove(e.Item);
}
}
}
}
}
}
protected void MyMenu_DataBound(object sender, EventArgs e)
{
Session["MenuLoaded"]=true;
}

会话变量的原因是——每次刷新/页面请求点击时,menuitemdatabound都会触发,我希望用户会话只加载一次菜单。

问题:

"删除项"代码运行良好。当用户登录时,菜单项不会根据需要显示。但当他点击一个现有项目移动到另一个页面时,所有菜单都会再次出现在菜单栏中。

为什么会发生这种情况。每次刷新页面或请求新url时,我是否应该允许menuitemdatabound事件。这不对。是吗?。但还有其他办法吗?或者我可以删除会话条件。

使用C#

尝试过:

page_load()
{
if(Session["sMenuLoaded"]==null)
lstRoles = (List<tblRoles>)Session["sMenuLoaded"];
else
{
Menu mainMenu = (Menu)Session["sMenuLoaded"];
mainMenu.DataBind();
}
}
mymenu_menuitemdatabound()
{
//  remains the same as above
}
mymenu_databound()
{
Session["sMenuLoaded"] = (Menu)Page.Master.FindControl("menuBar");
}

如果您使用的是xml站点地图,还有另一种方法可以实现它。Asp.Net具有覆盖站点地图逻辑的机制。有基本SiteMapProvider和默认实现XmlSiteMapProvider。基本SiteMapProvider具有IsAccessibleToUser。你可以创建自己的网站地图提供商,如下所示:

public class MySiteMapProvider : XmlSiteMapProvider 
{
public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
{
var lstRoles = (List<tblDetail>)context.Session["sRoles"];
// when we are accessing ChildNodes, it will execute the same IsAccessibleToUser method for each of this sub nodes
var childs = node.ChildNodes;
var isParentNode = node["isParent"] == "true";
if (childs.Count == 0 && isParentNode)
{
// it means that this is node is parent node, and all it sub nodes are not accessible, so we just return false to remove it
return false;
}
if (string.IsNullOrWhiteSpace(node.Description))
return true;
var formId = Convert.ToInt32(node.Description);
foreach (var item in lstRoles)
{
if (item.FormID == formId)
return true;
}
return false;
}
}

然后您可以在web.config中指定它:

<siteMap defaultProvider="myProvider">
<providers>
<add name="myProvider" securityTrimmingEnabled="true"  
type="WebApplication5.MySiteMapProvider"  siteMapFile="web.sitemap" />
</providers>
</siteMap>

然后,当Asp.Net基于站点地图呈现菜单时,它会每次为每个用户调用IsAccessibleToUser来验证它是否有权访问它。如果你在该方法中返回false,那么这个节点及其子节点将被隐藏。

更新1

我已经更新了代码以反映绑定的原始想法。为了测试,我使用了这样的web.sitemap

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="" title="Root">
<siteMapNode url="Default.aspx" title="1"  description="1">
<siteMapNode url="SubDefault.aspx" title="2"  description="11" />
<siteMapNode url="SubDefault1.aspx" title="3"  description="12" />
</siteMapNode>
<siteMapNode url="Default2.aspx" title="2_1" isParent="true">
<siteMapNode url="Default3.aspx" title="21" description="2"/>
</siteMapNode>
</siteMapNode>

以下是我的测试角色:

Session["sRoles"] = new List<tblDetail>()
{
new tblDetail() { FormID = 12 }, 
new tblDetail() { FormID = 1 }
};

此外,securityTrimingEnabled="true"已添加到上述示例中的web.config中。现在,当它检查每个节点时。如果节点没有根据会话中的tblDetail进行访问,则IsAccessibleNode返回false,并且该节点和所有子节点都隐藏在UI上。它将为每个节点执行此方法。在我的测试用例中,只显示Default.aspx和SubDefault1.aspx节点,因为我只指定了两个FormID1、11。因此MySiteMapProvider只显示了它们。

UPDATE2添加了我使用的aspx UI

<asp:Menu ID="Menu1" runat="server" DataSourceID="SiteMapDataSource1"></asp:Menu>
<asp:SiteMapDataSource ID="SiteMapDataSource1" Runat="Server" 
StartFromCurrentNode="False" ShowStartingNode="True" />

更新3

我已经为web.sitemap-isParent添加了新属性。如果所有子项都不可访问,则可以在应隐藏的节点中指定它。此外,我还更新了提供程序使用此isParent节点的代码。

成功登录后,您可以重新绑定菜单

Menu mainMenu=(Menu)Page.Master.FindControl("MyMenu");
if(mainMenu!=null)
{ 
mainMenu.DataBind();
}