One of the features in ASP.NET is the ability to use the Session object without using cookies ("cookieless Sessions"). When you enable cookieless Sessions in the Web.config, ASP.NET stores the SessionID in the URL using URL rewriting rather than placing the SessionId in a cookie. The SessionId is written into the URL right after the server name and the site name.
Using cookieless Sessions has an unfortunate side effect: You must also stop using the ASP.NET 2.0 menu controls. If you continue to use the menu controls, you'll find that each time your users click on a menu to go to another page, they lose their Session data. ASP.NET security, which by default uses an encrypted cookie to store security information, also has a cookieless option that embeds the encrypted security information into the URL. If you turn that option on and continue to use the ASP.NET 2.0 menu controls, you'll find that your users get logged off every time they click on a menu control.
The problem is that, essentially, the menu controls turn into a group of hyperlinks in the browser. The menu controls don't take the cookieless options into account so that, when these hyperlinks are generated at run time, the hyperlinks that make up the menus don't include the information rewritten into the URL to support either cookieless Sessions or cookieless security. The problem is that that the default provider (XMLSIteMapProvider) uses absolute URLs (ones beginning "http://www.mysite.com//...) based on the page's location as established at design time. As a result, these URLs cut out the information added through rewriting.
There is a solution: you can replace the default provider that supports the menus with one that does preserve the Session and security information by using relative URLs (e.g. that just reference the page name and any subfolder: "MySubFolder/MyPage.asp"). Relative URLs are, effectively, "added onto" the existing URL after the server name, the site name, and the Session/Security information.
Here's the code for that provider. Just create a class module (which will end up in your App_Code folder) and paste this code into it:
using System;
using System.Web;
public class RelativeURLSiteMap : XmlSiteMapProvider
{
public override System.Web.SiteMapNode CurrentNode
{
get
{
SiteMapNode cn = base.CurrentNode.Clone();
RelativizeUrl(cn);
return cn;
}
}
public override System.Web.SiteMapNode RootNode
{
get
{
SiteMapNode rn = base.RootNode.Clone();
RelativizeUrl(rn);
return rn;
}
}
public override System.Web.SiteMapNode GetParentNode(System.Web.SiteMapNode node)
{
SiteMapNode pn = base.GetParentNode(node).Clone();
RelativizeUrl(pn);
return pn;
}
public override System.Web.SiteMapNodeCollection GetChildNodes(System.Web.SiteMapNode node)
{
SiteMapNodeCollection cnc = base.GetChildNodes(node);
SiteMapNodeCollection newChilds = new SiteMapNodeCollection();
foreach(SiteMapNode tnode in cnc)
{
SiteMapNode cnode = tnode.Clone();
RelativizeUrl(cnode);
newChilds.Add(cnode);
}
return newChilds;
}
private void RelativizeUrl(SiteMapNode node)
{
if(node.Url.Length > 0)
{
node.Url = VirtualPathUtility.ToAppRelative(node.Url);
}
}
} // class RelativeURLSiteMap
To get your site to start using this provider, add these tags to your web.config file inside the <system.web> element:
<siteMap defaultProvider="RelativeURLSiteMapProvider">
<providers>
<add name="RelativeURLSiteMapProvider"
type="RelativeURLSiteMap"
siteMapFile="web.sitemap" />
</providers>
</siteMap>