Bookmarkable URLs in JSF 1.2
As often said, JSF 1.2 leaks on direct support of bookmarkable URLs. As a result it´s a common opinion that JSF applications cannot have bookarkable URLs at all or search engine optimization is hard to perform. But in general this is not correct. This articles shows a pattern of how to create bookmarable URLs in the pre JSF 2.0 era.
A bookmarkable URL means that you bookmark a link that can restore a page with it´s complete state. For such a bookmark you need an URL that may contain parameters as well,e.g.:
http://tech.top21.de/techblog/?entryId=4
The core problem with JSF and bookmarkability relies on the fact that it´s page navigation machanism is based on POST actions: Generated links that navigate to other views (through firing actions) always post back on the view which is currently displayed. The outcome of action methods controls the ongoing pageflow by specified navigation rules.
Well this is the standard way about how pageflow works in JSF, but it´s not the only possibility - and it´s not a must. YES YOU CAN have GET requests in JSF - even with parameters and automated assignment of these values to the model.
The following shows the basic pattern how to achieve this:
- use <h:outputLink> instead of <h:commandLink> (or <h:commandButton>)
- <h:outputLink> can have parameters which you can define by using <f:param>
- assign URL parameters to your model by using the managed property feature of JSF in combination with the param scope.
In a small xhtml example it looks like this:
<h:outputLink value="/techblog/detail.xhtml"> <h:outputText value="got to blog entry" /> <f:param name="entryId" value="4" /> </h:outputLink>
Here, we navigate directly to "/techblog/detail.xhtml" (so we don´t use a commandLink and JSF navigation rules) and add the parameter entryId the the URL. The good thing with <f:param> is, that the value gets implicit URL encoded. In our example we have a numeric value so this is not important, but in other cases this might become useful.
Next comes the managed bean code that will be keep the URL parameter in the model:
public class MyBlogBean
{
int entryId;
...
public void setEntryIdByGET(String id)
{
if(id != null && id.length > 0)
{
try
{
this.entryId = Integer.parseInt(id);
}
catch(NumberFormatException e)
{
// the value cannot be converted, do some fallback here
}
}
}
...
}
You see a setter that has a String parameter and ends with ...ByGET. For us it pointed out to be a best practise to mark your methods for handling URL parameters in some way to separate them from the regular setters which would be something like setEntryId(int entryId). We also check if the parameter is empty that is when the parameter or it´s value is not present in the GET request (we only want it to be set when given).
Now we couple the view to the model by using a managed property in conjunction with the param scope in our faces-config.xml:
<managed-bean>
<managed-bean-name>myBlogBean</managed-bean-name>
<managed-bean-class>MyBlogBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
...
<managed-property>
<property-name>entryIdByGET</property-name>
<value>#{param.entryId}</value>
</managed-property>
...
</managed-bean>
And that´s it! This way, the value will be set on the bean bean on it´s creation and can be used in the ongoing request to load some data, e.g. from a database.
Well, there´s some drawbacks when performing navigation in JSF like this:
- You cannot control your navigation by using rules and actions, the pageflow is defined in the view code (xhtml).
- Because we do not have an action method, loading of data must be performed elsewhere e.g. in a getter-method. You have to be careful with this in consideration of performance, because getters might be invoked multiple times in the request lifecycle (don´t bother your database!).
- We do not have conversion and validation features here, that´s why we have to do that work manually in setters-methods.
- To use GET navigation in combination with Input components (e.g. you want to send a formular by GET) the described approach is not enough. You also need a mechanism that converts POST requests with its submitted parameters to GET requests (POST-Redirect-GET) before this pattern can be used. There´s a nice article from ballusC on how to achieve this with an PhaseListener in JSF. He also mentions the pattern that is described in this article in the context of POST-Redirect-GET.
Conclusion
In JSF 1.2 bookmarkablity can be achieved. There are some drawbacks but the given possibilities are enough for implementing GET-based navigation in your application where needed. By the way this blog is completely realized by using this pattern. In JSF 2.0 bookmarability as being on of the most requested features by the JSF community was put into the spec. There you will find special action components and the concept of View-Parameters for supporting bookmarkable URLs without most of the drawbacks mentioned above.
Comments (1)
Matthias (posted on 06 Feb 2010, 10:18)
Good article! I only used post navigation so far which is not the best depending on the situation. But your "pattern" are a good way to realize get navigation in JSF.
Tell us what you think!