Bookmarking and the post-redirects-to-get pattern in Rails
Jan 11 2010

Post Redirects to Get is a common pattern seen in most web applications. If a POST changes the server state, eg. by storing a value in the database then it should redirect to a GET so that when a user refreshes the page, the browser doesn't POST the data again. All good until now, but what happens when a POST does not change the server state and this is interesting when it comes to bookmarking such pages.

Here is how in Rails a simple scaffolded User controller looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
users_controller.rb
def new
  @user = User.new
  respond_to do |format|
    format.html # new.html.erb
    format.xml  { render :xml => @user }
  end
end
def create
  @user = User.new(params[:user])
  respond_to do |format|
    if @user.save
      flash[:notice] = 'User was successfully created.'
      format.html { redirect_to(@user) }
      format.xml  { render :xml => @user, :status => :created, :location => @user }
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
    end
  end
end

The two actions create and new are of interest to us here. Now when:

  • User is on the New User page at /users/new
  • Submits the form with invalid data
  • The form is re-rendered with errors, the url has changed to /users/.

Here we are in a state where we are on a URL which cannot be bookmarked to get us to the same page. Now, what is the correct solution here. We could

  • We could redirect to new, but then we would lose all validation errors, or
  • Store the full error messages in flash and redirect to new, or
  • Store the invalid object in flash, and then retrieve it in the new action to show the errors.

This decision really comes down to how important bookmarking of pages is in your application. All three options introduce unnecessary code kludge - and are probably not worth it, ie. I would prefer breaking bookmarking in some scenarios in the app. For me, this is really a 'good to know' for post production error diagnosis, when you look at exception logs which show GETs on URLs which expect only POSTS. I've seen this done not only by browsers but also by search bots.

comments powered by Disqus