使用路由约束可以很好地解决这个问题。
使用路由约束
正如rails routing guide 所建议的那样,您可以通过检查路径是否属于语言或类别的方式定义路由约束。
# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Language.where(name: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Category.where(name: request[:category]).any? }
顺序定义了优先级。在上面的示例中,如果语言和类别具有相同的名称,则该语言将胜出,因为它的路径定义在类别路径之上。
使用永久链接模型
如果您想确保所有路径都是唯一的,一种简单的方法是定义 Permalink 模型并在那里使用验证。
生成数据库表:rails generate model Permalink path:string reference_type:string reference_id:integer && rails db:migrate
并在模型中定义验证:
class Permalink < ApplicationRecord
belongs_to :reference, polymorphic: true
validates :path, presence: true, uniqueness: true
end
并将其与其他对象类型相关联:
class Language < ApplicationRecord
has_many :permalinks, as: :reference, dependent: :destroy
end
这还允许您为记录定义多个永久链接路径。
rails_category.permalinks.create path: 'rails'
rails_category.permalinks.create path: 'ruby-on-rails'
使用此解决方案,路由文件必须如下所示:
# config/routes.rb
# ...
get ':language', to: 'top_voted#language', constraints: lambda { |request| Permalink.where(reference_type: 'Language', path: request[:language]).any? }
get ':category', to: 'top_voted#category', constraints: lambda { |request| Permalink.where(reference_type: 'Category', path: request[:category]).any? }
并且,作为在控制器中使用 can gem 和 load_and_authorize_resource 的其他用户的旁注:在调用 load_and_authorize_resource 之前,您必须通过永久链接加载记录:
class Category < ApplicationRecord
before_action :find_resource_by_permalink, only: :show
load_and_authorize_resource
private
def find_resource_by_permalink
@category ||= Permalink.find_by(path: params[:category]).try(:reference)
end
end