如何:以编程方式修改内存中的站点地图节点

更新:2007 年 11 月

网站经常使用动态 URL,其中包含作为查询字符串附加的信息。例如,新闻组或论坛的站点可能包含指向论坛或小组的静态 URL 以及每篇文章的动态 URL。一篇文章的 URL 可能为下面的格式:https://www.microsoft.com/newsgroups/ShowPost.aspx?ForumID=2&PostID=53

通过更新站点地图来列出每篇文章的 URL 并不是一种很有效的办法,原因是不能以编程方式将节点添加到站点地图中。但是,当用户查看文章时,可以使用 SiteMapPath 控件显示上溯至根节点的导航路径,并在该路径的各个链接中动态附加查询字符串,以标识文章、论坛或组。例如,到上述文章的导航路径可能如下所示:

主页 > 论坛列表 > 文章列表

站点地图“文章”节点的静态 Url 属性可能设置为 https://www.microsoft.com/newsgroups/ShowPost.aspx。但是在内存中,可以修改 SiteMapPath 控件中的 URL,以标识论坛和特定的文章。

可以使用 SiteMapResolve 事件在内存中更改站点地图节点,如下面的过程和示例所示。

以编程方式更改站点地图节点

  1. 在 Web 窗体页面的代码中,创建一个处理 SiteMapResolve 事件的方法。例如,下面的声明创建一个名为 ExpandForumPaths 的方法。

    private SiteMapNode ExpandForumPaths(Object sender, 
                                         SiteMapResolveEventArgs e)
    
    Private Function ExpandForumPaths(ByVal sender As Object, ByVal e As SiteMapResolveEventArgs) As SiteMapNode
    
  2. 在事件处理程序中,获取对当前节点的引用并克隆该节点。例如,如果节点是新闻组张贴,则代码可能如下面所示。

    SiteMapNode currentNode = SiteMap.CurrentNode.Clone(true);
    SiteMapNode tempNode = currentNode;
    
    Dim currentNode As SiteMapNode = SiteMap.CurrentNode.Clone(True)
    Dim tempNode As SiteMapNode = currentNode
    

    tempNode 变量返回可以遍历的内存中的站点地图节点,同时修改每个 Url 属性或其他属性。nodeCopy 中的引用是单独维护的,原因是期望从事件处理程序中返回的值是对当前节点的引用。但是,将使用 tempNode 变量在导航结构中递归上移。

    ms178425.alert_note(zh-cn,VS.90).gif说明:

    因为克隆的节点独立于静态站点导航结构,所以对 Url 属性的更改并没有保存在内存或磁盘中。

  3. 更改当前节点及其父节点的 Url 属性,以包含列出文章、论坛和组标识符的查询字符串信息。

    例如,下面的代码示例假定存在三种获取这些标识符的方法。

    int forumGroupID = GetMostRecentForumGroupID();
    int forumID = GetMostRecentForumID(forumGroupID);
    int postID = GetMostRecentPostID(forumID);
    
    if (0 != postID)
    {
        tempNode.Url = tempNode.Url + "?PostID=" + postID.ToString();
    }
    
    if ((null != (tempNode = tempNode.ParentNode)) &&
        (0 != forumID))
    {
        tempNode.Url = tempNode.Url + "?ForumID=" + forumID.ToString();
    }
    
    if ((null != (tempNode = tempNode.ParentNode)) &&
        (0 != forumGroupID))
    {
        tempNode.Url = tempNode.Url + "?ForumGroupID=" + forumGroupID.ToString();
    }
    
    Dim forumGroupID As Integer = GetMostRecentForumGroupID()
    Dim forumID As Integer = GetMostRecentForumID(forumGroupID)
    Dim postID As Integer = GetMostRecentPostID(forumID)
    
    If Not (0 = postID) Then
        tempNode.Url = tempNode.Url & "?PostID=" & postID.ToString()
    End If
    
    tempNode = tempNode.ParentNode
    If Not (0 = forumID) And Not (Nothing = tempNode) Then
        tempNode.Url = tempNode.Url & "?ForumID=" & forumID.ToString()
    End If
    
    tempNode = tempNode.ParentNode
    If Not (0 = ForumGroupID) And Not (Nothing = tempNode) Then
        tempNode.Url = tempNode.Url & "?ForumGroupID=" & forumGroupID.ToString()
    End If
    
    ms178425.alert_note(zh-cn,VS.90).gif说明:

    使用 if 语句可以确保仅向现有站点地图节点添加查询字符串,而且仅在组、窗体和文章标识符可用时才添加。

  4. 通过使用下面一行代码来返回克隆的节点。

    return currentNode;
    
    Return currentNode
    
  5. 在 Page_Load 方法中注册您的事件处理程序。例如,您的代码可能类似于下面的示例。

    SiteMap.SiteMapResolve +=
      new SiteMapResolveEventHandler(this.ExpandForumPaths);
    
    AddHandler SiteMap.SiteMapResolve, AddressOf Me.ExpandForumPaths
    
    ms178425.alert_note(zh-cn,VS.90).gif说明:

    当站点地图提供程序访问 CurrentNode 属性时(例如当 SiteMapPath 控件正在呈现导航结构时),会引发 SiteMapResolve 事件。

  6. 在 Web 窗体页中添加一个 SiteMapPath 控件以查看导航结构。您的 SiteMapPath 控件可能如下所示。

    <asp:SiteMapPath
    id="SiteMapPath1"
    runat="server"
    RenderCurrentNodeAsLink="true" />
    
  7. 确保在站点地图文件中存在对应于您的 Web 窗体页的节点。例如,如果您的 Web 窗体页名为 ShowPost.aspx,则您的 Web.sitemap 文件可能如下所示。

    <?xml version="1.0" encoding="utf-8" ?>
    <siteMap>
      <siteMapNode
        title="Forum Group" 
        description="Forum Group List"
        url="default.aspx">
        <siteMapNode 
          title="Forum" 
          description="Forum List"
          url="ShowForum.aspx">
          <siteMapNode 
            title="Post" 
            description="Post List" 
            url="ShowPost.aspx" />
        </siteMapNode>
      </siteMapNode>
    </siteMap>
    

示例

下面的代码示例演示如何处理 ASP.NET 网页上的 SiteMapResolve 事件,以修改由 SiteMapPath 控件显示的目标 URL。在此示例中,当前页是联机公告板或论坛中的一篇文章所在的页。为呈现更多有意义的站点导航,由 SiteMapPath 控件所显示的节点的 URL 上附加有与上下文相关的查询字符串。使用下面的代码呈现该控件。

<asp:SiteMapPath
id="SiteMapPath1"
runat="server"
RenderCurrentNodeAsLink="true" />
<asp:SiteMapPath
id="SiteMapPath1"
runat="server"
RenderCurrentNodeAsLink="true" />

当运行一个示例时,将光标置于 SiteMapPath 控件中链接的上方,可查看 URL 的更改。

此示例并不在 Web.sitemap 文件中添加 SiteMapNode 项目;Web.sitemap 文件只能手动编辑。

ms178425.alert_note(zh-cn,VS.90).gif说明:

SiteMapResolveEventHandler 内访问 CurrentNode 属性是安全的。在这种情况下,ASP.NET 站点导航基础结构能够防止无限递归。

此示例假定您已经有一个有效的站点地图文件,并且当前页在站点地图结构中的节点深度至少为 3。有关创建站点地图的更多信息,请参见 ASP.NET 站点地图

Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

    ' The ExpandForumPaths method is called to handle
    ' the SiteMapResolve event.
    AddHandler SiteMap.SiteMapResolve, AddressOf Me.ExpandForumPaths

End Sub

Private Function ExpandForumPaths(ByVal sender As Object, ByVal e As SiteMapResolveEventArgs) As SiteMapNode
    ' The current node represents a Post page in a bulletin board forum.
    ' Clone the current node and all of its relevant parents. This
    ' returns a site map node that a developer can then
    ' walk, modifying each node.Url property in turn.
    ' Since the cloned nodes are separate from the underlying
    ' site navigation structure, the fixups that are made do not
    ' effect the overall site navigation structure.
    Dim currentNode As SiteMapNode = SiteMap.CurrentNode.Clone(True)
    Dim tempNode As SiteMapNode = currentNode

    ' Obtain the recent IDs.
    Dim forumGroupID As Integer = GetMostRecentForumGroupID()
    Dim forumID As Integer = GetMostRecentForumID(forumGroupID)
    Dim postID As Integer = GetMostRecentPostID(forumID)

    ' The current node, and its parents, can be modified to include
    ' dynamic querystring information relevant to the currently
    ' executing request.
    If Not (0 = postID) Then
        tempNode.Url = tempNode.Url & "?PostID=" & postID.ToString()
    End If

    tempNode = tempNode.ParentNode
    If Not (0 = forumID) And Not (tempNode Is Nothing) Then
        tempNode.Url = tempNode.Url & "?ForumID=" & forumID.ToString()
    End If

    tempNode = tempNode.ParentNode
    If Not (0 = ForumGroupID) And Not (tempNode Is Nothing) Then
        tempNode.Url = tempNode.Url & "?ForumGroupID=" & forumGroupID.ToString()
    End If

    Return currentNode

End Function



...


' These methods are just placeholders for the example.
' One option is to use the HttpContext or e.Content object
' to obtain the ID.
Private Function GetMostRecentForumGroupID() As Integer
    Return 24
End Function

Private Function GetMostRecentForumID(ByVal forumGroupId As Integer) As Integer
    Return 128
End Function

Private Function GetMostRecentPostID(ByVal forumId As Integer) As Integer
    Return 317424
End Function
private void Page_Load(object sender, EventArgs e)
{
    // The ExpandForumPaths method is called to handle
    // the SiteMapResolve event.
    SiteMap.SiteMapResolve +=
      new SiteMapResolveEventHandler(this.ExpandForumPaths);
}

private SiteMapNode ExpandForumPaths(Object sender, SiteMapResolveEventArgs e)
{
    // The current node represents a Post page in a bulletin board forum.
    // Clone the current node and all of its relevant parents. This
    // returns a site map node that a developer can then
    // walk, modifying each node.Url property in turn.
    // Since the cloned nodes are separate from the underlying
    // site navigation structure, the fixups that are made do not
    // effect the overall site navigation structure.
    SiteMapNode currentNode = SiteMap.CurrentNode.Clone(true);
    SiteMapNode tempNode = currentNode;

    // Obtain the recent IDs.
    int forumGroupID = GetMostRecentForumGroupID();
    int forumID = GetMostRecentForumID(forumGroupID);
    int postID = GetMostRecentPostID(forumID);

    // The current node, and its parents, can be modified to include
    // dynamic querystring information relevant to the currently
    // executing request.
    if (0 != postID)
    {
        tempNode.Url = tempNode.Url + "?PostID=" + postID.ToString();
    }

    if ((null != (tempNode = tempNode.ParentNode)) &&
        (0 != forumID))
    {
        tempNode.Url = tempNode.Url + "?ForumID=" + forumID.ToString();
    }

    if ((null != (tempNode = tempNode.ParentNode)) &&
        (0 != forumGroupID))
    {
        tempNode.Url = tempNode.Url + "?ForumGroupID=" + forumGroupID.ToString();
    }

    return currentNode;
}


...


// These methods are just placeholders for the example.
// One option is to use the HttpContext or e.Content object
// to obtain the ID.
private int GetMostRecentForumGroupID()
{
    return 24;
}

private int GetMostRecentForumID(int forumGroupId)
{
    return 128;
}

private int GetMostRecentPostID(int forumId)
{
    return 317424;
}

安全性

使用查询字符串和窗体数据的一个重要方面就是验证传递的数据。ASP.NET 提供了一组验证控件,用于提供一种易用但功能强大的检错方式,并在必要时向用户显示错误信息。在上述示例中并未使用验证控件,目的是将任务集中于更改站点地图节点。有关如何向代码添加验证的信息,请参见验证 ASP.NET 控件

请参见

概念

ASP.NET 母版页和内容页中的事件

保证 ASP.NET 站点导航的安全

保证数据访问的安全

参考

SiteMapResolve

SiteMapNode

SiteMapPath

Context

其他资源

验证 ASP.NET 控件

处理和引发事件

寄宿环境中的 ASP.NET 应用程序安全性