First of all, JSF is a HTML code generator. So it's not different in JSF than in "plain" HTML. You should just not look at file system structure in webapp project when creating links in HTML. You should look at public URL structure of those resources. It's namely the webbrowser who has got to invoke and download those resources, not the webserver. The webbrowser knows absolutely nothing about the file system structure in the webserver. This is not specific to JSF projects. This applies to all web projects.
Relative URLs are not relative to their location in the file system structure in webapp project. They are relative to the request URL of the currently opened HTML document, exactly the one you see in browser's address bar. Noted should be that when a <base>
element is present in the HTML document, then all relative URLs in the HTML document not starting with /
will become relative to it.
Given a webapp which is configured with FacesServlet
mapping URL pattern of *.xhtml
, and is deployed to localhost:8080
with a context path of /context
, the URL of a /index.xhtml
file in project's web root will be as below:
http://localhost:8080/context/index.xhtml
---- -------------- ------- -----------
| | | `-- resource
| | `-- path (can be multiple folders)
| `-- domain (and port)
`-- scheme
When you're currently in http://localhost:8080/context/index.xhtml
, and you want to create a link to http://localhost:8080/context/calculate/calculate.xhtml
, then all the ways below will ultimately point to exactly the same absolute URL.
A relative URL starting with //
is relative to the current scheme.
<a href="//localhost:8080/context/calculate/calculate.xhtml">link</a>
A relative URL starting with /
is relative to the domain.
<a href="/context/calculate/calculate.xhtml">link</a>
A relative URL not starting with /
is relative to the path.
<a href="calculate/calculate.xhtml">link</a>
And when you're currently in
http://localhost:8080/context/calculate/calculate.xhtml
, and you want to link to http://localhost:8080/context/index.xhtml
, then the same rules apply:
A relative URL starting with //
is relative to the current scheme.
<a href="//localhost:8080/context/index.xhtml">link</a>
A relative URL starting with /
is relative to the domain.
<a href="/context/index.xhtml">link</a>
A relative URL not starting with /
is relative to the path.
<a href="../index.xhtml">link</a>
As you probably realize by now, a relative URL starting with /
is not dependent from the current path and domain. So, that's the URL you really want to use everywhere in your web application without worrying about maintenance trouble when changing domain or moving around files in the server. The only thing left is the dynamicness of the context path. You probably already know that this value is not controllable from inside the webapp. You'd really like to avoid hardcoding it. You can however easily let JSF print it programmatically with a little help of EL. It's namely just available by HttpServletRequest#getContextPath()
and the HttpServletRequest
is in EL available as implicit object #{request}
.
<a href="#{request.contextPath}/index.xhtml">link</a>
<a href="#{request.contextPath}/calculate/calculate.xhtml">link</a>
It only gets tedious to repeat this everytime. Fortunately, JSF offers the <h:link>
component for the very purpose of generating a HTML <a>
element with the current context path automatically inlined.
<h:link value="link" outcome="index.xhtml" />
<h:link value="link" outcome="calculate/calculate.xhtml" />
Note that the outcome
must represent a JSF view ID, which is not necessarily the same as the URL path (it will be when you map FacesServlet
on *.xhtml
). You can even omit the file extension here, JSF will automatically detect it as part of "implicit navigation" mechanism.
<h:link value="link" outcome="index" />
<h:link value="link" outcome="calculate/calculate" />
See also: