Ajax and the Post/Redirect/Get Pattern

I’ve been adding much more Ajax to our application over the last week and a half. In the past, I’ve done little Ajax things using DWR, but now I want to redraw parts of my UI that have been initially rendered using JavaServer Faces. So I’ve grabbed RichFaces and plopped that into the app. One of the things that I like about Ajax is the way it cleans up simple post requests.

One of the areas that usually comes up in web applications is the behaviour of the app w.r.t. the back-button. And there are two things that usually make back-button behaviour odd are:

  1. post requests
  2. no-cache directives

The second item (no-cache directives) exist in almost all secure web applications. In the first case, we often want post requests, even though they screw up back-button behaviour. Consider the following case:

You have a simple login page. You need to provide a userid/password in order to log in to the web application. Now, login is one of those cases where you always always always want to submit your form using method="POST" rather than method="GET". If you don’t, you run the risk of exposing the password on the URL field of the browser.

Now, suppose that the screen you see immediately after login is the application main page (say, for the sake of argument, something like this):

The problem with POST (in a no-cache environment) is that the page that you arrive after the post is hard to navigate to using the back-button. Consider the following steps:

  1. I get the login page
  2. I type in my userid/password
  3. I push the login button (which POSTs my credentials to the server)
  4. I get the main page displayed.
  5. I click on one of the options on the main page
  6. I change my mind and hit the back-button to go back to the main page

At this point, the browser will recognize that the main page is not cached (because of the no-cache directive) and that the only way to get a copy is to resubmit the request that originally drew the page. However, the people who invented the HTTP protocol made a key distinction between GET and POST requests: whereas GET is idempotent, POST is not. What this means is that browsers should not consider it safe to resend POST requests to the server. Which is why the browser pops up that annoying warning about resending data to the server.

Users are often really concerned about this pop-up. On our app, for example, the users raised a bug because they don’t like that pop-up. And rightly so, I think.

There’s a standard pattern for handling the “POST problem”: the “POST-redirect-GET” pattern. After you have to POST something to the server, the server sends a redirect to the next page. This pattern prevents bad back-button behaviour. It’s a nice pattern, but there are two problems:

  1. The app is made a little-bit slower because it takes two HTTP requests to get to the next page.
  2. It’s sometimes hard to completely stick to the POST-redirect-GET pattern, especially with new web frameworks like JSF and Tapestry, both of which seem too content with the POST method.

Consider the following example:

  1. I go to the login page
  2. I type in the wrong credentials and submit (POST) those to the server
  3. I am presented with a version of the login page with an error message.

Error cases are usually trickier to handle with the POST-redirect-GET pattern, and it’s often the case that the app doesn’t redirect in this situation. So we get a page that’s rendered as the output of a POST, and that’s likely a back-button problem.

But in an Ajax application, you can handle the POST via an Ajax request. RichFaces, which is what we’re using on our application, makes this especially easy. Our initial

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
<ui:composition template="/WEB-INF/layout/layout.xhtml">
  <ui:define name="title">Login</ui:define>
  <ui:define name="content">
    <h:form id="loginForm">
    <h:messages styleClass="errorText" />
    <table>
      <tr>
        <td>
          <h:outputLabel id="userLabel" for="user" styleClass="label" value="User" />
        </td><td>
          <h:inputText id="user" value="#{LoginController.user}" required="true" maxlength="255" />
        </td>
      </tr><tr>
        <td>
          <h:outputLabel id="passwordLabel" for="password" styleClass="label" value="Password" />
        </td><td>
          <h:inputSecret redisplay="false" value="#{LoginController.password}" id="password" required="true" maxlength="255" />
        </td>
      </tr><tr>
        <td></td>
        <td>
          <h:commandButton id="submit" value="Login" action="#{LoginController.login}" />
        </td>
      </table>
    </h:form>
  </ui:define>
</ui:composition>
</html>

Adding in RichFaces requires only a few simple revisions:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:a4j="http://richfaces.org/a4j"
      xmlns:rich="http://richfaces.org/rich"
      xmlns:f="http://java.sun.com/jsf/core">
<ui:composition template="/WEB-INF/layout/layout.xhtml">
  <ui:define name="title">Login</ui:define>
  <ui:define name="content">
    <h:form id="loginForm">
    <rich:messages styleClass="errorText" />
    <table>
      <tr>
        <td>
          <h:outputLabel id="userLabel" for="user" styleClass="label" value="User" />
        </td><td>
          <h:inputText id="user" value="#{LoginController.user}" required="true" maxlength="255" />
        </td>
      </tr><tr>
        <td>
          <h:outputLabel id="passwordLabel" for="password" styleClass="label" value="Password" />
        </td><td>
          <h:inputSecret redisplay="false" value="#{LoginController.password}" id="password" required="true" maxlength="255" />
        </td>
      </tr><tr>
        <td></td>
        <td>
          <a4j:commandButton id="submit" value="Login" action="#{LoginController.login}" />
        </td>
      </table>
    </h:form>
  </ui:define>
</ui:composition>
</html>

There’s really only three differences:

  1. We include the two namespaces for RichFaces (rich: and a4j: — the latter reflects the inclusion of the Ajax4Java package in RichFaces)
  2. We replaces the standard JSF messages component with a RichFaces messages component.
  3. We change the commandButton to the RichFaces command button.

Nothing changes in our backing beans, and RichFaces respects any redirects that we send back to the client. Unless there’s a redirect, our Ajax requests replace the form area with changed content (which is great for building wizards — we can hide all the POSTS between individual parts of the wizard, and then finally redirect out of the wizard at the end).

I’m inclined to wonder if there’s any case, now, for processing a POST in a synchronous request.

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply