Encode rails model id in URLs with base36

1.5k views Asked by At

I’m trying to encode the model id for a rails app using base36 following the first answer here but I am unsure as to what I need to do.

Where do I put id.to_s(36)? Do I need to add a column to my database?

I’d like to have my URLs to be domain.com/user/rD4g35tQ instead of domain.com/user/3.

Ruby 1.9.3 Rails 3.2.16

Here is my show action in my controller:

  def show
    @user = User.find(params[:id])
  end

Edit: Here is my create action:

  def create
    @user = User.new(params[:user])
    if @user.save
      sign_in @user
      redirect_to @user
    else
      render 'new'
    end
  end
2

There are 2 answers

2
Tim Peters On BEST ANSWER

to_param is for this. It is used by the routing to generate paths. By default it returns an object's id, but you can override it in a model to be anything you want:

class User < ActiveRecord::Base

  def to_param
    id.to_s(36)
  end

end

In controllers, params[:id] will now be the string you wanted, but you'll need to convert back to the real primary key:

def show
  @user = User.find(params[:id].to_i(36))
end
4
Sibevin Wang On

Yes, I would create a unique column, for example 'uuid', in the users table to store the encoded string and query a user with it.

in config/routes.rb

resources :users

in app/controllers/users_controller.rb

def show
  @user = User.where(uuid: params[:uuid]).first
end

def create
  @user = User.new(params[:user])
  if @user.save
    sign_in @user
    redirect_to user_path(@user)
  else
    redirect_to new_user_path
  end
end

Actually, the reason to use an encoded string instead of the id is for the security concern. I suggest not using the base36 encoding for this purpose. You can use a random string and assign it when a user is created, for example:

in app/models/user.rb

class User < ActiveRecord::Base

  attr_readonly :uuid

  before_validation :gen_uuid, on: :create

  validates :uuid, presence: true, uniqueness: true

  # ...

  def to_param
    self.uuid
  end

  private
  def gen_uuid
    self.uuid = RandomToken.genf(32)
  end
end

UPDATE:

The disadvantage of a customized route is to lose the _url and _path helpers, the better way is to keep the original routes created by resources and use Tim's answer to override the to_param.