一个常见REST应用场景的困惑和探究

来源:互联网 发布:网络棋牌代理赚钱吗 编辑:程序博客网 时间:2024/06/05 09:47
 下面是一个最常碰到的应用场景:
一个论坛应用(如: javaeye)。文章资源是一个嵌套关系: 栏目(board)-->分类(category)-->文章(topic),其routes.rb里的配置可能如下:
Java代码 复制代码
  1. map.resources :boards do |board|   
  2.   board.resources :categories do |category|   
  3.     category.resources :topics   
  4.   end   
  5. end  

列出某分类下的所有主题的URL则是这样的:http://***/boards/2/categories/4/topics

但是我们有时可能需要列出某栏目下的所有主题。于是可能需要这样的URL: http://***/boards/2/topics
要实现这样的URL的需要修改一下routes,如下。
Java代码 复制代码
  1. map.resources :boards do |board|   
  2.   <SPAN style="COLOR: red">board.resources :topics</SPAN>   
  3.   board.resources :categories do |category|   
  4.     category.resources :topics   
  5.   end   
  6. end  

但这样方式有点问题:在构造URL时 topics_path(@board,@category)有效,但topics_path(@board)无效。


但是......,我们可能还要搜索附合一些关键字条件的文章,也许需要这样的URL: http://***/boards/2/topics?keyword="rails ruby"
或者象javaeye现在的处理方式用URL:http://***/borads/ajax_search?keyword="rails ruby"


------------------------------------------------------------------
又或许我们就抛弃嵌套方案,直接定义topics资源,如下
Java代码 复制代码
  1. map.resources :topics  


以后删除和查看都很容易构造URL,只需要文章id就行了: http://***/topics/1
如果要查某栏目、某分类下的文章就在topics_controller里创建几个自定义action。

刚说没有可供参考的案例,突然想到了http://beast.caboo.se/,看看它是怎么实现的

它有这样的嵌套关系: forums--> topics -->posts (即 栏目-->帖子-->回复)
关于posts的routes如下。

Java代码 复制代码
  1. map.resources :forums, :has_many => [:posts] do |forum|   
  2.   forum.resources :topics, :name_prefix => nil do |topic|   
  3.     topic.resources :posts, :name_prefix => nil   
  4.     topic.resource :monitorship, :name_prefix => nil   
  5.   end   
  6. end   
  7.   
  8. map.resources :posts, :name_prefix => 'all_', :collection => { :search => :get }   
  9.   
  10. map.with_options :controller => 'posts', :action => 'monitored' do |map|   
  11.   map.formatted_monitored_posts 'users/:user_id/monitored.:format'  
  12.   map.monitored_posts           'users/:user_id/monitored'  
  13. end  



这里即有post嵌套关系,也有把 post直接定义成资源,也有传统的controller/action定义。


在其网页上有一个搜索帖子的form表单,其代码如下:

Java代码 复制代码
  1. <form action="/posts/search" method="get">           
  2.    <input id="search_box" name="q" size="15" type="text">   
  3. </form>  



这应该是用了routes里的post第二条的定义。
post_controller有一个search方法,如下:

Java代码 复制代码
  1. def search   
  2.   conditions = params[:q].blank? ? nil : Post.send(:sanitize_sql, ["LOWER(#{Post.table_name}.body) LIKE ?""%#{params[:q]}%"])   
  3.   @posts = Post.paginate @@query_options.merge(:conditions => conditions, :page => params[:page], :count => {:select => "#{Post.table_name}.id"})   
  4.   @users = User.find(:all, :select => 'distinct *', :conditions => ['id in (?)'@posts.collect(&:user_id).uniq]).index_by(&:id)   
  5.   render_posts_or_xml :index   
  6. end  



另外,post_controller里还有第三条routes定义的action----monitored,没细看它是做什么用的。


http://beast.caboo.se/posts
http://beast.caboo.se/forums/1/posts
http://beast.caboo.se/forums/1/topics/1208/posts
都能用,它们都是指定post_controller的index方法,这个方法里构造SQL条件挺巧妙的,

Java代码 复制代码
  1. def index   
  2.   conditions = []   
  3.   [:user_id, :forum_id, :topic_id].each { |attr| conditions << Post.send(:sanitize_sql, ["#{Post.table_name}.#{attr} = ?", params[attr]]) if params[attr] }   
  4.   conditions = conditions.any? ? conditions.collect { |c| "(#{c})" }.join(' AND ') : nil   
  5.   @posts = Post.paginate @@query_options.merge(:conditions => conditions, :page => params[:page], :count => {:select => "#{Post.table_name}.id"})   
  6.   @users = User.find(:all, :select => 'distinct *', :conditions => ['id in (?)'@posts.collect(&:user_id).uniq]).index_by(&:id)   
  7.   render_posts_or_xml   
  8. end  

出处:http://www.javaeye.com/topic/118887