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

本文探讨了Rails中通过routes.rb配置资源的关系,并介绍了如何通过嵌套和非嵌套方式定义资源,实现不同层级资源间的URL构造及搜索功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 下面是一个最常碰到的应用场景:
一个论坛应用(如: 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  
  
    map.resources :boards do |board|
      board.resources :categories do |category|
        category.resources :topics
      end
    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  
  
    map.resources :boards do |board|
      board.resources :topics
      board.resources :categories do |category|
        category.resources :topics
      end
    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  
  
    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  
  map.resources :forums, :has_many => [:posts] do |forum|
    forum.resources :topics, :name_prefix => nil do |topic|
      topic.resources :posts, :name_prefix => nil
      topic.resource :monitorship, :name_prefix => nil
    end
  end

  map.resources :posts, :name_prefix => 'all_', :collection => { :search => :get }

  map.with_options :controller => 'posts', :action => 'monitored' do |map|
    map.formatted_monitored_posts 'users/:user_id/monitored.:format'
    map.monitored_posts           'users/:user_id/monitored'
  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>  
   <form action="/posts/search" method="get">        
      <input id="search_box" name="q" size="15" type="text">
   </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  
  def search
    conditions = params[:q].blank? ? nil : Post.send(:sanitize_sql, ["LOWER(#{Post.table_name}.body) LIKE ?", "%#{params[:q]}%"])
    @posts = Post.paginate @@query_options.merge(:conditions => conditions, :page => params[:page], :count => {:select => "#{Post.table_name}.id"})
    @users = User.find(:all, :select => 'distinct *', :conditions => ['id in (?)', @posts.collect(&:user_id).uniq]).index_by(&:id)
    render_posts_or_xml :index
  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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值