jsfbase-urlcontextpath

How get the base URL via context path in JSF?


I have this structure:

WebContent
    resources
        components
            top.xhtml

    company
        about_us.xhtml

    index.xhtml

top.xhtml is a component, that is used in index.xthml and about_us.xhtml too.

top.xhtml

<ul>
    <li><a href="index.xhtml">Home</a></li>
    <li><a href="company/about_us.xhtml">About us</a></li>
    ...
</ul>

So my problem is, when the current page is index.xhtml the component generates URLs correctly, but when the current page is about_us.xhtml, it generates wrong URLs. I cannot use relative path because it's going to generate the wrong URL too. I think it is because the component is based on the current path of the *.xhtml page.

The only solution I could found out is:

<ul>
    <li><a href="${pageContext.request.contextPath}/webname/index.xhtml">Home</a></li>
    <li><a href="${pageContext.request.contextPath}/webname/about_us.xhtml">About us</a></li>
    ...
</ul>

But I think is not 'elegant' at all. Any ideas?


Solution

  • URLs are not resolved based on the file structure in the server side. URLs are resolved based on the real public web addresses of the resources in question. It's namely the webbrowser who has got to invoke them, not the webserver.

    There are several ways to soften the pain:

    JSF EL offers a shorthand to ${pageContext.request} in flavor of #{request}:

    <li><a href="#{request.contextPath}/index.xhtml">Home</a></li>
    <li><a href="#{request.contextPath}/about_us.xhtml">About us</a></li>
    

    You can if necessary use <c:set> tag to make it yet shorter. Put it somewhere in the master template, it'll be available to all pages:

    <c:set var="root" value="#{request.contextPath}/" />
    ...
    <li><a href="#{root}index.xhtml">Home</a></li>
    <li><a href="#{root}about_us.xhtml">About us</a></li>
    

    JSF 2.x offers the <h:link> which can take a view ID relative to the context root in outcome and it will append the context path and FacesServlet mapping automatically:

    <li><h:link value="Home" outcome="index" /></li>
    <li><h:link value="About us" outcome="about_us" /></li>
    

    HTML offers the <base> tag which makes all relative URLs in the document relative to this base. You could make use of it. Put it in the <h:head>.

    <base href="#{request.requestURL.substring(0, request.requestURL.length() - request.requestURI.length())}#{request.contextPath}/" />
    ...
    <li><a href="index.xhtml">Home</a></li>
    <li><a href="about_us.xhtml">About us</a></li>
    

    (note: this requires EL 2.2, otherwise you'd better use JSTL fn:substring(), see also this answer)

    This should end up in the generated HTML something like as

    <base href="http://example.com/webname/" />
    

    Note that the <base> tag has a caveat: it makes all jump anchors in the page like <a href="#top"> relative to it as well! See also Is it recommended to use the <base> html tag? In JSF you could solve it like <a href="#{request.requestURI}#top">top</a> or <h:link value="top" fragment="top" />.