This article was published on March 1st 2019 and received its last update on August 1st 2020. It takes about 2 minutes to read.
How to preserve the query string when redirecting routes in a Rails application
Redirecting a route in a Rails app while keeping the original request's query string intact is no default behaviour, but it can be done quite easily with a little thought.
Table of contents
Rails' provides the redirect
method (defined in the ActionDispatch::Routing::Redirection
module) which allows us to redirect any URL to another one from within our routes.rb
file:
Unfortunately, this method discards certain parts of the original URL, as the docs clearly state:
get "/stories" => redirect("/posts")
[The
redirect
method] will redirect the user, while ignoring certain parts of the request, including query string, etc./stories
,/stories?foo=bar
, etc. all redirect to/posts
.
There are several use cases where this is undesired behaviour — we often need to keep the original request's query string.
The inline method
We can preserve the original request's query string by calling the redirect
method with a block:
get '/stories', to: redirect { |params, request| URI.parse(request.original_url).query ? "/posts?#{URI.parse(request.original_url).query}" : "/posts" }
While this inline trickery certainly works, it becomes messy once we need to redirect more than one route.
A more DRY and elegant approach is writing our own redirector class.
The redirector class
Redirector classes are usually rather small, so defining them in our application's routes.rb
file does not add a lot of clutter and makes sense:
class QueryRedirector
def call(params, request)
uri = URI.parse(request.original_url)
if uri.query
"#{@destination}?#{uri.query}"
else
@destination
end
end
def initialize(destination)
@destination = destination
end
end
A redirector class needs to implement a call
method which receives two arguments: The symbolized path parameters and the request object (as we already saw when we called the redirect
method with a block).
The implementation is up to us. In this example, our redirector checks the original request for query parameters and if it finds any, it appends them to the new destination we need to provide to its initializer.
Use the class in our routes
In order to use it, we provide the redirect
method with a new QueryRedirector
instance:
get "/stories", to: redirect(QueryRedirector.new("/posts"))
This will redirect /stories?foo=bar
to /posts?foo=bar
, but /stories
to /posts
(no unnecessary ?
because the original request does not have a query string).
Get in the loop
Join my email list to get new articles delivered straight to your inbox and discounts on my products.
No spam — guaranteed.
Got it, thanks a lot!
Please check your emails for the confirmation request I just sent you. Once you clicked the link therein, you will no longer see these signup forms.