URLs in XPages
Tue Oct 01 20:28:59 EDT 2013
The topic of URLs came up today in a chat and in the most recent NotesIn9, and I think this is an area that deserves some particular attention. When designing any web app, it's easy to fall into some traps with URLs, and writing XPages code juggles things around a bit - some things are much easier than usual, some are insidiously harder.
At a base level, there are four main types of URLs you'll use when writing a web app, distinguished by how they begin (don't quote me on the names, or even whether or not they're technically called "URLs"):
-
Full-protocol URLs beginning with a protocol followed by a colon, such as
https://frostillic.us/f.nsf/Home.xsp
-
Protocol-relative URLs beginning with a double-forward-slash, such as
//frostillic.us/f.nsf/home.xsp
(these are fairly rare) -
Server-relative URLs beginning with a forward-slash, such as
/f.nsf/home.xsp
-
Page-relative URLs beginning with basically anything else, such as
home.xsp
As a general rule, page-relative URLs are only a good idea if you are absolutely certain of the structure of your app relative to the URL the browser is using. The tricky part here is that it's very easy to write such a URL that works when you test it (say, pointing from one XPage to another with a URL like <xp:link value="otherXPage.xsp" text="Click me"/>
), but fails in other situations. For example, see how such a URL would work when visiting each of these URLs, which all point to the same XPage:
- http://frostillic.us → http://frostillic.us/otherXPage.xsp
- http://frostillic.us/f.nsf → http://frostillic.us/otherXPage.xsp
- http://frostillic.us/f.nsf/ → http://frostillic.us/f.nsf/otherXPage.xsp
- http://frostillic.us/f.nsf/Home.xsp → http://frostillic.us/f.nsf/otherXPage.xsp
- http://frostillic.us/f.nsf/Home.xsp/this/is/a/functioning/url → http://frostillic.us/f.nsf/Home.xsp/this/is/a/functioning/otherXPage.xsp
Yes, the last one is legal - you can get to the stuff after the ".xsp" via facesContext.getExternalContext().getRequest().getPathInfo()
.
Fortunately, the XPage environment provides a nice solution to this problem: when using XSP elements, it converts the third type (server-relative URLs) from being relative to the server to be relative to the app. This is almost always1 what you want. So if I write a link as <xp:link value="/otherXPage.xsp" text="Click me"/>
, it will work correctly in all of those situations.
Note the opening caveat: you have to be using XSP elements. Fortunately, this covers all WYSIWYG use and pretty much everything you're going to do elsewhere. It's rare that you actually want a stock-HTML <a/> instead of an <xp:link/>, and this includes resource URLs in an XPage, custom control, or theme. So, as a rule, you should almost always preface your in-app links with a forward slash to piggyback on this feature.
There are two cases that need special treatment: when you want to link out of your app from an XPage element and when you want to link into your app from a non-XPage element. Fortunately, there are two bits of non-obvious code that will do these jobs easily and reliably:
-
To break out of the app-relative "jail" in an XPage element, prefix your URL with "/.ibmxspres/domino/". For an explanation as to what this stands for and a list of similar prefix URLs, read Mastering XPages. For now, just know that that prefix means "get me out of here". So to link to, say, the directory, you could write a link like
<xp:link value="/.ibmxspres/domino/names.nsf" text="Domain's Directory"/>
. -
To reliably reference the current app from a non-XPage link, use this bizarre EL formulation:
${facesContext.externalContext.requestContextPath}
. What that gets you is basically like @WebDbName prefixed with a slash. So, for example, if you wanted to reference a video attached as a file resource, you could do something like<video src="${facesContext.externalContext.requestContextPath}/someVideo.mp4" />
.
Keep in mind that the special XPage behavior applies only to when you're writing XSP markup (or otherwise pulling URLs into XPage elements); it doesn't apply to, for example, rich text or other HTML brought into the page.
And on a final, self-aggrandizing note, the "mentoring" offer in that NotesIn9 applies to me and my company. Feel free to contact us or get in touch with me directly if you're ever in need.
1. Except when writing CSS files. In CSS, URLs are relative to the location of the CSS file. This is usually just fine, since you, not the browser, control the URLs you use to refrence the CSS file. It gets tricky when you use resource aggregation, though.