Obfuscation of URLs - rails

2019-08-14 16:29发布

问题:

I'm working on a project that's left me stumped, hoping someone out there might have some interesting input. I see there are several gems available for obfuscation of urls, but they seem to stop at the slug level instead of the controller level - i.e. www.foo.com/mycontroller/8sZ16lp. I'm looking for a method to produce something along the lines of www.foo.com/8asd31Ud, dropping the controller name. I checked the docs of the obsufacate_id gem, but it doesn't appear to go that far.

To give more background - I'm really hoping to have www.foo.com/mycontroller/15/edit = www.foo.com/95Ali32

回答1:

As this doesn't match the convention of rails RESTful URL's my guess is you'll likely just need to write your own routes and maybe more. Out of curiosity, how would you envision that the system knows what type of object to load with "95Ali32"? This might be better to build with Sinatra or something that gives you more control over the routing and less convention.

Here's one possible approach that uses a table with your slugs that maps to type and object id:

# migration
create_table :slugs do |t|
  t.string :object_type, :null => false
  t.string :object_id, :null => false
  t.string :slug
  t.timestamps
end

# models
class Slugs < ActiveRecord::Base
  belongs_to :object, :polymorhic => true
end

class AModel < ActiveRecord::Base
  has_one :slug, :as => :owner
end

# routes.rb
# note that anything else should go above this because this will catch all other URL's
get '*slug', to: 'slugs#show'

# controller
class SlugsController < ApplicationController
  def show
    @object = Slug.where(slug: params[:slug])
    raise ActiveRecord::NotFound.new unless @object
    render @object.kind
  end
end

You would then need to build views for each type of object. See this related question


Update

Here's another idea. How obscure do you need the slug? What if each model has a known code and then the ID is encoded some way and appended to the model code? Then you could do something much simpler in the code using routes that are preconfigured:

# You could generate this too, or just hard-code them
prefixes = ['8sZ', '95Ali']
[:a_model, :another_model].each do |model|
  match "#{prefixes.pop}:id", :controller => model.to_s.underscore.pluralize, :action => :show, :as => model
end 

This would give you routes like these

/8sZ1 #=> AModelsController#show(:id => 1)
/95Ali341 #=> AModelsController#show(:id => 341)

You could take this another level and use friendly_id to generate slugs for the model ID's. Or use UUID's instead of integer ID's (supported by PostgreSQL).