当我们从模型ID转向slugs时,我们必须注意控制器动作的名称碰撞。
前段时间,我写了关于在Rails中实现slugs的最简单方法。虽然实现起来很简单,但这并不是我们要注意的全部。一旦我们从ID转移到slugs,我们需要确保我们的用户不会选择会破坏应用程序的slugs。
/teams/:slug 路径可以覆盖或被/teams/controller_action_name 覆盖。
下面的片段将是一个好的开始,因为字母数字的限制对一个URL标识符来说是很好的。
class Team < ApplicationRecord
...
validates :slug,
presence: true,
uniqueness: true,
length: {minimum: 2, maximum: 30},
format: {with: /\A[a-zA-Z0-9]+\Z/}
但这是不够的。输入的slugs仍然可能与常规的应用路径发生冲突。如果有人选择你的控制器动作的名称呢?或者你想为未来保留的东西?
为了解决这个问题,我们可以为这个目的写一个自定义验证器。
# app/validators/restricted_paths_validator.rb
class RestrictedPathsValidator < ActiveModel::Validator
RESTRICTED_PATHS = TeamsController.action_methods + [
"admin",
"admins"
]
def validate(record)
if RESTRICTED_PATHS.include?(record.slug)
record.errors.add :slug, :restricted_path
end
end
end
然后把它包含在你的模型中。
# Avoid slug conflicts with routes
validates_with ::RestrictedPathsValidator
验证器是限制当前所有来自控制器的动作,更多的路径可以在上面添加。
如果我们想提供一个自定义的错误信息,我们可以添加一个I18n条目,activerecord.errors.messages.restricted_path 。
en:
activerecord:
errors:
messages:
restricted_path: not allowed