Reconstructing Request URIs in Rails

When building AideRSS we needed to process full URIs as query strings in our app (for example: http://www.aiderss.com/all/igvita.com/) - sounds simple, but unfortunately the Rails magic was getting in the way! Routing code was splitting the request URL, and removing all dynamic parameters! Let's try to convince Rails to do otherwise.

Grabbing 'the rest' of the request

By default, Rails will perform a split on the forward slash ('/') character when it tries to disassemble the request URI. Hence, a query for /all/igvita.com is treated as three different parameters, whereas we really want everything after /all/ to appear as a single parameter. Thankfully, a quick regular expression will get us this far:

map.with_options :controller => 'main' do |main|
  main.connect '/all/:source', :action => 'all', :source => /.*/
end

The regular expression will try to match any string or character after /all/, resulting in a full query string of igvita.com! All done, right? Not quite, the solution above still won't capture any dynamic parameters. A request for igvita.com/index.php?posts=best still results in a route for igvita.com/index.php, and the rest is conveniently forgotten and treated as parameters to our Rails application.

Rebuilding dynamic parameters

To get around this one, we can add a before filter into our controller:

def rebuild_dynamic_parameters
    # assuming the route with /.*/ match is present, and points to :source
    @uri = params[:source]

    # omit true Rails query parameters, and append the rest back to the URI
    query_string = params.select {|k,v| not %w[action controller source].include?(k)}
    unless query_string.empty?
      query_params = query_string.collect {|k,v| "#{k}=#{v}"}
      @uri << "?" << query_params.join("&")
    end
end

Perhaps not the most elegant solution, but it should do the trick. One small caveat: if you have a submit form, like AideRSS, chances are it will have a name, and it will append itself to the query string - you might want to omit it by specifying the name of the submit button in the array of Rails specific parameters above.

Ilya GrigorikIlya Grigorik is a web ecosystem engineer, author of High Performance Browser Networking (O'Reilly), and Principal Engineer at Shopify — follow on Twitter.