用户形象图片

Rails URL


有些人认为 Rails 的有些部分是“丑陋的”并且要大幅度“清洁”在 URL :/accounts/edit/12 内的数字身份。

URL 被认为极重要资产。不仅仅是因为用户必须一直看着它们,而是因为搜索引擎给予了它足够的重要性:因为它是“有限的资源”,你只能包括几个关键字,所以你最好使用事关重要的关键字。

Rails 努力帮助你能够使用完美的,整洁的 URL ,但遇到 :id 就不再是这样了。但它这样做有一个充足的理由。也就是说,如果它被用于一个用户的登录的话,若不使用它的数字 ID,那么当用户修改它的登录时,原有 URL 将不能再合法地登录。是的,我可以认同你说的用户不能修改登录,但是对于一个 Blog 的标题呢?或一个人的全名呢?或一个项目名称呢?一旦你在 URL 内使用一些用户编辑的信息片断,那么你创建的 URL 状态就有问题,并且这甚至比丑陋的的 URL 更糟。

但这儿有个非常简单的解决办法。同时使用一个永久性的 id 和一个完美的原文描述。为什么不使用 /accounts/edit/12-john-doe 来代替丑陋的 /accounts/edit/12 或脆弱的 /accounts/edit/john-doe 呢。你的代码有它需要查寻的用户 12”,即使它以后更改它的名字为 john-d-doe ,而且你的用户及搜索爬虫都有 john-doe”来使用。

实现上述想法很简单,因为 Rails :id 看做是路由内的一个特殊的参数。它的特殊性是因为它试图在你创建 URL 时,在被传递的所有对象上调用 to_param 方法。这就说明了为什么 url_for :id => @account 等效于 url_for :id => @account.id ,因为活动记录模型有一个缺省的 to_param 方法,它返回对象的 id

你所要做的就是为你的模型定义自己的 to_param() 方法,并确保你没有明确地在你的 url_for link_to 内包含 .id ,因为包含的话你将会跳过对你自己的 to_param 方法的调用。

class Account < ActiveRecord::Base

def to_param

"#{id}-#{full_name.gsub(/[^a-z1-9]+/i, '-')}"

end

end

当然了,这种解决办法的第二部分是,确保你的动作能够处理这些扩展的 :ids。聪明的你可能会立即想到给活动记录的 find 方法打补丁,以整理调用内的参数像 Account.find(params[:id]) 这样。还是忘掉这种愚笨的处理方式吧,试着先用一下,当看到它不可思议地像期待的那样工作时不要惊讶哟。

看到了嘛,这只是巧合;它并未被设计成 Rails 的一部分。Rails 只是将你的长 :id 字符串传递给数据库服务器,由于数据库看到的 id 列实际上是个整数,它会在使用之前试着把参数转换成一个数字,并且该转换会只使用任何它找到的数字化字符并去掉其余非数字化字符,即转换 “12-john-do” 12 。看看,配置上的偶然为之;还有什么比这更好?

当然,你也放想添加两三个单元测试,只是为了确保无论何种数据库服务器你都能以这种特殊方式来工作。我不确定它是不是 SQL-92 标准的一部分,但让我惊奇的是,所有不同的主流数据库服务器都可工作。

现在你可以给你的 URL 添加些有用的信息,像“asbestos-mesothelioma-canada-drugs-viagra-ambien

当你知道“连字符/减号(-)”做为分隔符比其它任何字符都工作的要好时,你会感到惊奇。Google 将在像 canada_drugs”这样的 URL 上匹配 canada drugs”,但它不会单独匹配 canada 。如果你使用连字号,像 canada-drugs”,Google 会认为它们的分隔的,是独立的单词。

更新:留言中的一些好观点。

首先,是 Aristotle Pagaltzis 在六个月前都提出了同样的观点。

其次,你或许想从任何有效部分(如,ID 是正确的,但其余则不是)使用重定向到“官方的” URL 。实现起来很简单,但要每个控制器内要有一些额外代码。

第三,一些数据库服务器不完成类型强制转换,如果你传递的 id 查询不是一个整数,会让它抱怨。Postgres 就是个例子。对它的解决办法也相当简单:只要在你的应用程序开始运行时,确保这个代码被执行(把它放到 lib 内并从 environment.rb 文件内 require 它,或做成一个插件,等等)

class ActiveRecord::Base

def self.find_from_ids_with_coercion(id, options)

find_from_ids_without_coercion(id.to_i, options)

end

alias_method :find_from_ids_without_coercion, :find_from_ids

alias_method :find_from_ids, :find_from_ids_with_coercion

end

下面是一些跟留言:

1、我有一些问题。如果 URL 内有其它数字会发生什么?例如,“12-john<CHMETCNV tcsc="0" numbertype="1" negative="True" hasspace="False" sourcevalue="23" unitname="”">-23</CHMETCNV>。还有为什么有额外参数不是更好吗,如“edit/12?john-doe ”?

答复:数字的转换实际上在发现第一个非数字字符时就中止了。因此“12-25”被转换成“12”。

使用一个额外参数意味着明确地传递它给 url_for/link_to ,这不会带来不便。

2、我还没有观望。我的最大疑虑是,如果你有像 /users/12-john-doe 这样 URL ,但是 /users/12-big-poop-face 也一样会工作。这可不太好。

答复:The permanent redirect is definitelly the right thing to do if you really want to preserve the RESTfulness of your URLs.

http://www.notsostupid.com/blog/<CHSDATE isrocdate="False" islunardate="False" day="7" month="7" year="2006">2006/07/07</CHSDATE>/urls-on-rails/

回到帖子顶部