3

In my JSF application I've got a link to a page, which is build with a dynamic query string:

<h:link outcome="list?#{controller.queryString}" value="Example" />

I can't use <f:param>, because the count of included parameters changes for every request.

The problem is that the query string is URL encoded, because some parameter values can contain a =. The result is that the query string is encoded twice in the result HTML.

An outcome of list?a=b%3Dc becomes:

<a href=".../list.xhtml?a=b%253Dc">Example</a>

Looking at the JSF spec (Default NavigationHandler Algorithm) I can't find anything about encoded in the query string. But I think that com.sun.faces.application.NavigationHandlerImpl.findImplicitMatch is a rather hacky implementation.

My question: Did I use <h:link> in a wrong way (works as designed) or is this somehow a bug?

My current solution is to use a <h:outputLink> instead - which prevents the usage of any navigation rules.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Tobias Liefke
  • 8,637
  • 2
  • 41
  • 58

1 Answers1

3

If #{controller.queryString} is at least view scoped or broader, then you could use a Map<String, String> with unencoded values instead and use <c:forEach> to dynamically generate <f:param> entries.

private Map<String, String> params;
<h:link ...>
    <c:forEach items="#{bean.params}" var="entry">
        <f:param name="#{entry.key}" value="#{entry.value}" />
    </c:forEach>
</h:link>

If it is however request scoped (e.g. depends on user input), then you'd need to use a request scoped bean and explicitly rebuild the view as below in the action method before ajax-updating the link, so that JSTL tags run once again during render response.

String viewId = context.getViewRoot().getViewId();
UIViewRoot view = context.getApplication().getViewHandler().createView(context, viewId);
context.setViewRoot(view);

If the bean being request scoped is however not an option, then your best bet is manually generating the URL with help of ViewHandler#getBookmarkableURL(), passing the view ID and the parameters as a Map.

url = context.getApplication().getViewHandler().getBookmarkableURL(context, outcomeViewId, params, false);
<a href="#{bean.url}">...</a>

The outcome view ID is easy if you rely solely on implicit navigation.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555