在Rails中动态重新加载路由是一个坏主意吗?

我有一个应用程序,我正在编写,我允许管理员为页面,类别等添加别名,我想使用不同的控制器/操作取决于别名(没有重定向,我发现渲染实际上并没有调用方法。我只是渲染模板)。 我已经尝试了捕获所有路由,但我并不是因为导致并捕获每次都抛出的DoubleRenderexception而感到疯狂。

我提出的解决方案是在服务器启动时动态生成的路由,并在创建/更新/销毁别名时使用Alias模型的回调来重新加载路由。 这是我的routes.rb中的代码:

Alias.find(:all).each do |alias_to_add| map.connect alias_to_add.name, :controller => alias_to_add.page_type.controller, :action => alias_to_add.page_type.action, :navigation_node_id => alias_to_add.navigation_node.id end 

我在Alias模型中使用回调如下:

 after_save :rebuild_routes after_destroy :rebuild_routes def rebuild_routes ActionController::Routing::Routes.reload! end 

这是针对Rails的最佳做法吗? 有更好的解决方案吗?

快速解决方案

在routes.rb的底部有一个包罗万象的路线。 在路由路由的操作中实现所需的任何别名查找逻辑。

在我的实现中,我有一个表,它将定义的URL映射到控制器,操作和参数哈希。 我只是将它们从数据库中取出,然后调用相应的操作,然后尝试为该操作呈现默认模板。 如果动作已经呈现了某些内容,则抛出DoubleRenderError,我抓住并忽略它。

您可以将此技术扩展为您想要的复杂function,尽管随着它变得越来越复杂,通过调整路由或Rails默认路由逻辑来实现它更有意义,而不是通过自己重新实现所有路由逻辑。

如果找不到别名,则可以根据需要抛出404或500错误。

记住的东西:

缓存:不知道您的URL先验可以使页面缓存成为绝对的熊。 请记住,它基于提供的URI而不是url_for (:action_you_actually_executed )进行url_for (:action_you_actually_executed 。 这意味着如果你别名

 /foo_action/bar_method 

 /some-wonderful-alias 

你会在缓存目录中找到一些精彩的alias.html。 当你试图扫描foo的栏时,你不会扫描该文件,除非你明确指定它。

容错:检查以确保有人不会在现有路由上意外混淆。 你可以通过强制所有别名进入“目录”来做到这一点,这个目录已知不会被路由(在这种情况下,别名在文本上是唯一的,足以确保它们永远不会发生冲突),但这不是最理想的我能想到的一些应用程序的解决方案。

本,

我发现你已经使用的方法是最好的。 使用Rails 3,您必须稍微更改一下代码,以便:

 MyNewApplication::Application.reload_routes! 

就这样。

首先,正如其他人所建议的那样,在routes.rb的底部创建一个catch-all路由:

 map.connect ':name', :controller => 'aliases', :action => 'show' 

然后,在AliasesController中,您可以使用render_component来呈现别名操作:

 class AliasesController < ApplicationController def show if alias = Alias.find_by_name(params[:name]) render_component(:controller => alias.page_type.controller, :action => alias.page_type.action, :navigation_node_id => alias.navigation_node.id) else render :file => "#{RAILS_ROOT}/public/404.html", :status => :not_found end end end 

我不确定我是否完全理解这个问题,但您可以在控制器中使用method_missing然后查找别名,可能是这样的:

 class MyController def method_missing(sym, *args) aliased = Alias.find_by_action_name(sym) # sanity check here in case no alias self.send( aliased.real_action_name ) # sanity check here in case the real action calls a different render explicitly render :action => aliased.real_action_name end def normal_action @thing = Things.find(params[:id]) end end 

如果你想优化它,你可以在method_missing放置一个define_method ,所以它只会在第一次调用时“丢失”,并且从那时起就是一个普通的方法。