在 Rails 2.0 中如何避免在 URL 使用資料流水號
我曾寫過一篇「Rails: 建立 Permalink,避免流水號洩漏網站資料」針對有人擔心(或質疑)Ruby on Rails 中以資料表主鍵流水號作為 URL 中的參數,會導致網站洩漏某些不希望被外界一眼看穿的資訊,例如使用者總數或文章總數之類的數據,因此簡單地透過 Model 在存取資料庫時的 before_create 方法建立每一筆資料的 unique key 來避免這個問題。
之前使用 Rails 1.2.x 版本時根據我當時的作法並無不妥,因為我自己的 coding 習慣是會完整地指定 controller, action 以及 id,並在對應的 action 中使用 find_by_key 的方法來找到資料。
然而,升級到 Rails 2.0 後便會出現許多麻煩,例如預設產生的 scaffold 必須修改許多地方,才能讓 Model.find_by_key、redirect_to post_url(@post) 之類的方法正確運作。
於是我一直到前陣子才找到應該比較正規的作法:在 Model 中使用 to_param 方法。
to_param(): Enables Active Record objects to be used as URL parameters in Action Pack automatically.
這麼好用的方法我居然不是一開始就發現,只能說相見恨晚,好險我開發 Rails 2.0 的時間還不算長,沒走到太多冤枉路。要實踐本標題所說的在 Rails 2.0 中如何避免在 URL 使用資料流水號便相當簡單!
首先建立 Scaffold:(通常會對 key 這個欄位建立 index 並設定字串長度 limit,別忘了)
script/generate post title:string content:text key:string
接著將 Post 的 migration 寫進資料庫後修改 Post model 如下:
class Post < ActiveRecord::Base
before_create :generate_key
def self.find(*args)
if args.first.is_a?(String)
find_by_key(args.shift, *args) or raise ActiveRecord::RecordNotFound
else
super
end
end
def to_param
key
end
protected
# 我前一篇文章產生 key 的方式不太一樣,這應該是個人習慣。
# 另外,其實亂碼的網址沒有 SEO 的效益,建議還是弄個 slug 吧!
def generate_key
require 'digest/sha1'
self.key = Digest::SHA1.hexdigest(Time.now.to_s.split(//).sort_by {rand}.join )[15..24]
end
end
就這樣!概念很簡單,就是不再用 ID 當查詢的參數,全面改用 key 啦!關鍵就在於使用 to_param()
方法以及對 find()
做進一步的判斷,除了 Model.find(:all)
之類的 Symbol,其餘的 CRUD 都是傳入 Key 作為參數,因此在此僅簡單地判斷是否為字串。
如此一來,不管你是什麼 user_posts_url
, new_user_post_url
都可以輕鬆地直接以 ActiveRecord Object 傳遞,例如使用 edit_post_url(@post.key)
這種方式傳值,非常麻煩、要改 Controller 又要改 View,現在用了 to_param()
,什麼都不用改了!又優雅又簡單,寫起來更加快樂、效率也提昇了: p
P.S. 其實在 Rails 1.2.x 應該也是這麼做比較優雅,不限於 2.0