POST Redirects in Rails

Every so often I see someone on a blog, mailing list, or discussion board asking how to use redirect_to in Rails to send a POST request. The HTTP/1.1 protocol states that browsers should only follow GET or HEAD redirects without prompting for the user’s permission. In practice, most modern browsers just convert all redirects to a GET request. Thus, we can’t rely on a client-side redirect to help us out.

There’s nothing that says we can’t simulate this behavior server-side though. Let’s create a controller method called redirect_post to handle the bulk of the work.


  def redirect_post(redirect_post_params)
    controller_name = redirect_post_params[:controller]
    controller = “#{controller_name.camelize}Controller”.constantize
    # Throw out existing params and merge the stored ones
    request.parameters.reject! { true }
    request.parameters.merge!(redirect_post_params)
    controller.process(request, response)
    if response.redirected_to
      @performed_redirect = true
    else
      @performed_render = true
    end
  end

The code should be fairly straightforward. Pass the redirected post params to the controller’s process method and handle the response. If the response redirected then follow that redirect otherwise render the contents of the response.

So how is this useful? Let’s say you’re using RESTful routes and you present a read-only version of a site to anonymous users. If the user clicks a button that would normally create or update some content you want to redirect them to the login screen and then proceed with the action (real-world example). Without the server-side POST redirect we would need the user to login and then retry their previous action.

Here’s some simple authentication code showing how to use redirect_post for this purpose:


  def login_required
    current_user || store_and_redirect
  end

  def store_and_redirect(default = new_login_url)
    if request.method == :post
      session[:return_post_params] = params
    end
    session[:return_to] = request.request_uri
    redirect_to default
  end

  def redirect_back(default = welcome_path)
    return_params = session[:return_post_params]
    session[:return_post_params] = nil
    if return_params
      redirect_post(return_params)
    else
      redirect_to(session[:return_to] || default)
    end
    session[:return_to] = nil
  end

Basically, store the POST params and inject them back into the request object after a successful login. Change the defaults on the store_and_redirect and redirect_back methods to match your application. Protect the controller methods that require login with a login_required before_filter. After a successful login, call redirect_back.

Now if an anonymous user clicks a button that POSTs to a method requiring login, they get sent through the login process and we process the original POST controller action transparently. There are other alternatives but for our particular use this seemed like the cleanest solution.

What’s your preferred solution?

Edited 5/28: After greater understanding of Rails internal I’ve updated the redirect_post method to fix a bug with the handling of non-200 response codes.

One Response to “POST Redirects in Rails”

  1. Taryn East Says:

    Neat solution!

    “#{controller_name.camelize}Controller”.constantize
    hmm - looks like Inflector needs a “controllerize” ;)