If you need to reference SharePoint’s _layouts path to point to an application page that you’ve built, it’s tempting to simply go <a href=”/_layouts/…”> as this works in most cases. It works fine when referencing images for example, which just need to be loaded from the filesystem, and /_layouts will get you there.
A problem creeps in though when using this method to point to pages (e.g. aspx/ashx) that reference SharePoint entities such as lists or list items.
You see, if your custom code is executed within a site collection with a different URL to the root, like say http://myserver/sites/MySiteCollection, and you’re using SPContext.Current.Site to reference the current site collection, your code could break when trying to load a list or another entity via its ID, because that list may not exist in the site collection that is referenced.
I ran into this problem recently when passing an SPWeb ID to my custom application page via the querystring. I wanted to instantiate the web, so I got a reference to SPContext.Current.Site and attempted to load the web, but the code threw an exception and complained (much to my disgust) that the web could not be found. It turns out that this was because it was physically located in a site collection at a different URL from the one I used to reference my page. My bad.
Because the _layouts folder is a virtual path, it can generally be accessed from any URL, even from a location that doesn’t actually exist (e.g. http://myserver/ThisIsNotARealSite/_layouts/images/ANNOUNCE.GIF). This makes it easy to reference stuff in the _layouts folder from anywhere, but it can give the false impression that you can reference your aspx pages using any URL and everything will be hunky dory. As I learned, it does matter what URL you use to reference your application pages.
So if you’re using SPContext.Current in your custom application pages be aware that your context is derived from that URL location, and when constructing URLs, always make sure that you reference the page at the correct site collection location. For example, http://myserver/sites/MySiteCollection/_layouts/MyCustomPage.aspx.
One way to point to your _layouts folder correctly is to use the same simple method that SharePoint uses internally, which I’ve delivered to you courtesy of .NET Reflector.
string layoutsPath = web.ServerRelativeUrl + (web.ServerRelativeUrl.EndsWith("/") ? string.Empty : "/") + "_layouts";
Yes, it’s a bit crappy to have to construct URLs in this way (instead of simply hard-coding /layouts in your hrefs), but it ensures that your custom code will always be executed under the correct site collection.