Ensuring a Trailing Slash in Rails W/Out Dependencies
For some reason this is more difficult in rails than it should be.
The semantic value of the concept of having a trailing slash in the URL is basically one of representing a folder, a virtual directory. In RESTful terms, this represents a collection route, or an :index action. Exhibit A:
GET /products/
This page will list many products as links. In restful terms, this will lead to the :show action. In this instance, we are representing a final destination, a member route. This dictates no trailing slash, semantically speaking:
GET /products/some-permalink
GET /products/some--ther-permalink
So how to deal with this in Rails? Rails will naturally favor slash-less versions of all URLs, unless specified otherwise. Step one is to generate all internal links the correct way, to do this, simply pass the :trailing_slash => true key-value pair to your named route. You ARE using named routes like you should be, right? Good.
member_path(object, :trailing_slash => true) or
collection_path(:trailing_slash => true)
This will generate the correct link. Excellent. However, when you write your routing or functional tests, like you should be, testing the extreme input, you will notice that the slash-less routes work just fine and Rails acts as if everything is normal.
I expected to be able to find a way to deal with this cleanly, by accessing a hash of sorts in the Rails’ request object, but nothing came up. the params hash omits this. As does request.request_uri, request.path and even request.fullpath. Well, shiet, what’s a bro to do?
Well, take a look at my solution in the form of a pair of private methods in ApplicationController:
class ApplicationController < ActionController::Base
private
def ensure_trailing_slash
redirect_to url_for(params.merge(:trailing_slash => true)), :status => 301 unless trailing_slash?
end
def trailing_slash?
request.env['REQUEST_URI'].match(/[^\?]+/).to_s.last == '/'
end
end
Then, when we need to use this in any actual controller, we simple declare the following:
class ObjectController < ApplicationController
before_filter :ensure_trailing_slash, :only => :index
[...]
end
Notice I am using 301 redirects here, as a sound SEO measure.
Hope this helped, until next time.