How get the base URL via context path in JSF?
Asked Answered
I

2

73

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?

Inconformity answered 29/7, 2011 at 19:49 Comment(0)
G
157

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" />.

Gannie answered 29/7, 2011 at 20:0 Comment(13)
thank you so much, i'm a big fan of yours =) i hope be a great developer as you are mate, thanks.Inconformity
Nice solution, didn't know that was possible! thanks for sharing!Verlaverlee
Thanks, i have a follow-up question: the correct path for the link would be "/appname/faces/page.xhtml", but with #{request.contextPath} i only get /appname. is there a way to get the /faces/ too? where is that even defined?Amigo
@styx: use #{request.servletPath} or use <h:link>.Gannie
@Gannie thanks, very helpful as usual :) i needed the first one, since i use the path inside a <meta http-equiv="refresh"> tag. thanks again!Amigo
@BalusC: I tried your ui:param approach & it does work with Mojarra but not with Myfaces 2.1.8. I created an issue thinking it was bug but learnt from there that <ui:param> should be direct child of <ui:include> or <ui:define> to work. See this issue for the discussion on that.Marchioness
@balusC yes, the ui:param approach doesn't work with myfaces.Nadya
@Aklin: I fixed the answer, thank you, I wasn't aware about this being Mojarra specific, I've actually also never seen a Mojarra issue report about this.Gannie
<c:set var="root" value="#{request.contextPath}/" /> does not seem to make the #{root} work for all the included pages. I works only when #{root} is used within the defined page only..Marchioness
@Aklin: Perhaps it's another MyFaces thing again. Try adding scope="request".Gannie
Thanks, after adding scope="request" now it works! However I was wondering I should use application scope for this particular case since the path would remain the same for all requests & all users application wide !?Marchioness
Notice that the attempt to set "base" as shown above will not work as expected in an proxy-environment (example: apache with mod_proxy, forwarding somedomain/app to somedomain:8080/app; above attempt will set "base" to somedomain:8080/app instead of -somehow- expected somedomain/app.Fawn
I am working on a legacy application with JSF1.0. value="#{facesContext.externalContext.requestContextPath}/ worked for me, while value="#{request.contextPath}/" was not working.Chasseur
O
1

JSTL 1.2 variation leveraged from BalusC answer

<c:set var="baseURL" value="${pageContext.request.requestURL.substring(0, pageContext.request.requestURL.length() - pageContext.request.requestURI.length())}${pageContext.request.contextPath}/" />

<head>
  <base href="${baseURL}" />
Omnidirectional answered 17/6, 2015 at 4:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.