Redirecting old ID routes to new slugs
I just updated my models to use slugs by installing the Stringex gem. Now I wanted to redirect those old URLs that my users know and use to the new slug URLs.
/answers/3 => /answers/everything
But since someone could come up with some full digit answer title (whatever the question) we should still allow full digit URLs like:
/answers/42
Sure, in the unlikely case that someone clicked on an outdated link like /answers/42 with 42 being not a slug, but an ID from before the route changed - while in the meantime someone else created a new answer with that digit title, he will not get the answer he was looking for. But hey, it'll be still an answer!
Solution
Disable the default #show route and create a #show route that accepts an ID or slug.
# config/routes.rb
resources :answers, :except => :show
get "answers/:id_or_slug" => 'answers#show', :as => :answer
Then enhance your controller's #show action to redirect with a 301 (Moved Permanently) if no instance was found using the slug, but using the ID. And raise a RoutingError if no instance was found at all.
# app/controllers/answers_controller.rb
def show
@answer = Answer.find_by_url(params[:id_or_slug])
if @answer.nil?
@answer = Answer.find_by_id(params[:id_or_slug])
if @answer.nil?
raise ActionController::RoutingError.new('Not Found')
else
redirect_to @answer, :status => 301
end
end
end
Note: If you're using resource loaders like CanCan, you'll need to disable resource loading for the action #show:
# app/controllers/answers_controller.rb
load_and_authorize_resource :except => :show, :find_by => :url
authorize_resource :only => :show
Written by Robert Wünsch
Related protips
2 Responses
hey great tip! though if you go to a url with an id the url in the browser is still the id route. how would you go about changing that? been working on a fix for my site, because i dont want people to copy the old links, and share them.
@koryteg The above solution teaches users the new slug URLs by always redirecting them to the slug URLs (like /answers/everything) when they used the id (such as /answers/42). So the old id URLs will never be made public by your own website. There's always that redirect.