Friday, February 12, 2010

RESTful GET URLs with JSF 2.0

JSF 2.0 introduces Bookmarkable URLs with so-called "view params", when combined with the new h:link component, can be used to implement RESTful GET URLs.

Traditionally JSF is using POST exclusively. It was possible to use GET but in practice this required some workarounds, especially because "page load action" is not natively supported by JSF. In effect, making Ruby on Rails-style controller ridiculously hard to implement.

JSF 2.0 improves this. Let's see how it's done.

A JSF-style show entity link looks like this:
<h:commandLink action="#{campaignController.prepareView}"
  value="Show"/>
Where CampaignController.prepareView() essentially sets the current entity bean that will be displayed by the next view:
public String prepareView() {
    current = (Campaign)getItems().getRowData();
    selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
    return "View";
}
This works, but it's quite overkill to use HTTP POST just for showing a page of entity info. To change it to GET, we use h:link :
<h:link outcome="ViewOne" value="Show Campaign">
  <f:param name="campaignId" value="#{item.id}" />
</h:link>
This will generate a URL like http://localhost:8080/app/faces/campaign/View?campaignId=7

We'll need to process this new parameter "campaignId", by declaring a viewParam inside f:metadata in the destination JSF view (ViewOne.xhtml):
<f:metadata>
  <f:viewParam name="campaignId" value="#{campaignController.campaignId}"/>
</f:metadata>
The campaignController.campaignId refers to a property, therefore we create the setter and getter in the controller class:
public Long getCampaignId() {
    if (current != null) return current.getId();
    else return null;
}

public void setCampaignId(Long campaignId) {
    current = ejbFacade.find(campaignId);
}
That's it!

The URL is still not pretty / SEO-friendly, but for that purpose we can use PrettyFaces.

Related resources: