Reputation: 898
On Rails 3, I'm trying to redirect from a URL without a trailing slash to the canonical URL that has a slash.
match "/test", :to => redirect("/test/")
However, the route above matches both /test and /test/ causing a redirect loop.
How do I make it match only the version without the slash?
Upvotes: 4
Views: 5174
Reputation: 9604
Bullet-proof solution:
before_action :force_trailing_slash
...
private
def force_trailing_slash
return if trailing_slash?
url = url_for \
request.path_parameters
.merge(request.query_parameters)
.merge(trailing_slash: true)
redirect_to url, status: :moved_permanently
end
def trailing_slash?
URI(request.original_url).path.ends_with? '/'
end
Upvotes: 1
Reputation: 9341
You can force the redirect at the controller level.
# File: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protected
def force_trailing_slash
redirect_to request.original_url + '/' unless request.original_url.match(/\/$/)
end
end
# File: app/controllers/test_controller.rb
class TestController < ApplicationController
before_filter :force_trailing_slash, only: 'test' # The magic
# GET /test/
def test
# ...
end
end
Upvotes: 6
Reputation: 1398
I wanted to do the same to have a cannonical url for a blog, this works
match 'post/:year/:title', :to => redirect {|env, params| "/post/#{params[:year]}/#{params[:title]}/" }, :constraints => lambda {|r| !r.original_fullpath.end_with?('/')}
match 'post/:year/:title(/*file_path)' => 'posts#show', :as => :post, :format => false
then I have another rule which deals with the relative paths inside the post. Order is important, so former goes first and generic one goes second.
Upvotes: 3
Reputation: 11811
Maybe it works with
match "/test$", :to => redirect("/test/")
Upvotes: 0
Reputation: 176372
There is an option in ActionDispatch called trailing_slash
you can use to force the trailing slash at the end of the URL. I'm not sure if it can be used in the routing definition.
def tes_trailing_slsh
add_host!
options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'}
assert_equal('http://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) )
end
In your case, the best way is to use Rack or your web server to execute the redirect. In Apache, you can add a definition such as
RewriteEngine on
RewriteRule ^(.+[^/])$ $1/ [R=301,L]
To redirect all routes without a trailing slash to the corresponding one with trailing slash.
Or you can use rack-rewrite to perform the same task in your Rails app at Rack level.
Upvotes: 2