Rails 3 的动态路由
Posted
技术标签:
【中文标题】Rails 3 的动态路由【英文标题】:Dynamic routes with Rails 3 【发布时间】:2011-05-10 21:36:36 【问题描述】:我的任务是按照路由模型开发 Rails 应用程序。
我需要有PageController
和Page
模型。页面 url 必须像 /contacts, /shipping, /some_page
。
我还需要CatalogController
和Category
模型。类别网址必须类似于/laptops, /smartphones/android
。
并且它将是ProductsController
和Product
型号,产品的url必须是/laptops/toshiba_sattelite_l605
,/smartphones/android/htc_magic
。
我知道这个问题可以通过使用类似的 URL 来解决
/page/shipping
/catalog/smartphones/android
但客户不希望看到在 URL 中插入“/page
”或“/catalog
”。
请告诉我解决这个问题的方向。 对不起我的英语不好。
【问题讨论】:
如何区分/something
是页面还是类别?
可能是类别(/laptops),也可能是页面(/contacts)。当然,我知道类别和页面模型需要有一些字段,比如“slug”,并且它必须在站点中是唯一的。
这是一个非常糟糕的主意。如果他们担心网址“丑陋”,那么他们可以执行/faq/contacts
或/questions/shipping
之类的操作。
查看官方 Rails 路由指南@guides.rubyonrails.org/routing.html
你找到解决方案了吗?我也很感兴趣......但谷歌没有帮助我:(我使用友好的_id,它生成了一个带有 slug 名称的表 slug,它是 id 和控制器名称(sluggable_type)字段...但没有文档显示如何将其用于路由目的。欢迎提供任何帮助。问候。
【参考方案1】:
Rails 路由仅基于 URL。如果名称前没有/pages
或/categories
或其他任何内容,那么Rails 将如何判断像http://example.com/smartphones
这样的URL 是页面URL 还是类别URL?除非您在路由条件中有一些预定义的页面名称列表,否则它不能。
在此示例中,唯一的方法是通过smartphones
实际搜索页面,如果没有找到,则假定这是一个类别名称并搜索一个类别。这不能在铁路路线中完成。为此,您需要一个控制器来处理所有这些 URL 并进行搜索,然后呈现适当的模板(您不能重定向,因为这会更改 url)。这是解决这个问题的一种丑陋的方法。
如果我是你,我会尝试说服客户同意某种前缀(如 /p_pagename
),或者最好是不同的 URL。对于页面,它可能类似于 /p/shipping
,所有其他 URL 都可以路由到 CategoriesController
或 ProductsController
。
【讨论】:
我同意你的立场,但绝对不可能说服客户同意 URL 中的某种前缀。他的 SEO 需要这样的 URL。所以我需要找到解决方案。【参考方案2】:你必须写一个“包罗万象”的规则:
在 routes.rb 上:
get '*my_precioussss' => 'sauron#one_action_to_rule_them_all'
你的宝贝控制器:
class SauronController < ApplicationController
def one_action_to_rule_them_all
@element = find_by_slug(params[:my_precioussss])
render @element.kind # product, category, etc
end
end
然后为每种“种类”的元素编写一个视图:product.html.erb、category.html.erb 等。
确保编写您的 find_by_slug
实现。
您可以将one_action_to_rule_them_all
更改为pikachu_i_choose_you
并将SauronController
更改为PokemonController
,也可以。
【讨论】:
仅根据示例进行投票 ;)【参考方案3】:你需要写一个catch all
match '*page' => 'pages#show'
然后在你的页面控制器中处理页面路径
def show
if params[:page]
@page = Page.find_by_page(params[:page])
else
@page = Page.find(params[:id])
end
...
如果您使用 Omniauth 或其他使用它自己的 rails lib,您可能需要从中排除一些 url,请在您的约束中使用 lambda
match '*page' => 'pages#show', :constraints => lambda|req| !req.env["REQUEST_URI"].include? "auth"
如果您想要嵌套路径(例如“smartphones/android”),您需要将 :page 参数分开并在控制器中处理路径段
path = :page.split("/") // you now have an array of path segments you can use in your controller
【讨论】:
【参考方案4】:我发现这份 rails 3 路由指南非常有用:
http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/
【讨论】:
【参考方案5】:我结合了两种方法来做到这一点
使用 friendly_id gem 在查找请求中处理 slug 而不是数字 id
用:constraints=>ConditionClass
定义路由,它必须返回true
或false
(所以路由将被确定或不)
小例子:
#routes.rb
class CategoryBrandGoodConstraint
def self.matches?(request)
path_parts = request.path.split('/')
Category.find(path_parts[1]).present?&&Brand.find(path_parts[2]).present?&&Good.find(path_parts[3]).present? rescue false
end
end
get ':category_friendly_id/:brand_friendly_id/:good_friendly_id' => 'goods#index', :constraints => CategoryBrandGoodConstraint
#another conditional routes with another :constraints=>ConditionClass2 definition
#...
#until you define all necessary conditional application routes
#goods_controller.rb
def index
#operate with params[:category_friendly_id], params[:brand_friendly_id], params[:good_friendly_id] to collect all you need for view rendering
end
当然,你可以路由到另一个控制器来渲染另一个视图get '/:whatever' => 'another_controller#another_action', :constraints=>ConditionClassN
希望对你有帮助!
干杯
【讨论】:
以上是关于Rails 3 的动态路由的主要内容,如果未能解决你的问题,请参考以下文章