论坛首页 Ruby版 rails

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

浏览 1606 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
最后更新时间:2007-08-31
下面是一个最常碰到的应用场景:
一个论坛应用(如: javaeye)。文章资源是一个嵌套关系: 栏目(board)-->分类(category)-->文章(topic),其routes.rb里的配置可能如下:
  
    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,如下。
  
    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资源,如下
  
    map.resources :topics

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


------------------------------------------------------------------

也许是我面向URL设计的思想没转过弯来,说实在的我倒觉得用REST越来越复杂了。主要是因为它即可以这样做,又可以那样做。有很多常见问题,并没有太多现存的解决的模式可供参考。
   
最后更新时间:2007-08-31
刚说没有可供参考的案例,突然想到了http://beast.caboo.se/,看看它是怎么实现的

它有这样的嵌套关系: forums--> topics -->posts  (即 栏目-->帖子-->回复)
关于posts的routes如下。
  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表单,其代码如下:
   <form action="/posts/search" method="get">        
      <input id="search_box" name="q" size="15" type="text">
   </form>


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

  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条件挺巧妙的,

  def index
    conditions = []
    [:user_id, :forum_id, :topic_id].each { |attr| conditions << Post.send(:sanitize_sql, ["#{Post.table_name}.#{attr} = ?", params[attr]]) if params[attr] }
    conditions = conditions.any? ? conditions.collect { |c| "(#{c})" }.join(' AND ') : nil
    @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
  end
   
0 请登录后投票
最后更新时间:2007-09-03
REST跟URL没有关系吧。
   
0 请登录后投票
最后更新时间:2007-09-03
liubin 写道
REST跟URL没有关系吧。


1.1 什么是REST?
REST这个术语,是Roy Fielding在Ph.D.论文中提出来的,它的全称是“Representational State Transfer.”
REST描述了这么一个架构:利用标准的http 协议中的 get, post, put, delete 来请求和操作网络上的资源。
    在REST中,资源的意思就是一个 基于URL实体,客户端可以通过 http协议来和它进行交互。这个资源可以用各种形式来展示给客户端,如 HTML,XML,RSS,主要依赖于客户端的调用方式。并不像以往的Rails开发那样,用REST方式,一个 url 不是指定一个 model 或者 action, 一个 url 仅仅是资源的本身而已。
   
0 请登录后投票
最后更新时间:2007-09-04
在路由定义里的has_many不起作用,原因是没有安装edge。

# map.resources :forums, :has_many => [:posts] do |forum| 
可以用以下替代。

#   forum.resources :forums do |forum| 
#     topic.resources :posts, :name_prefix => 'forum_'

在beast网站中,name_prefix大都是设为nil,我建议还是加一个前缀,否则在构造URL只能构造默认的一种。


今天 已经把我开发的网站全部转成REST,工作量都集中在把controller+action的url构造方式,改成*_*_path方式。
   
0 请登录后投票
最后更新时间:2007-11-23
提个问题,这种类型的嵌套资源怎么表示:
class Post < ActiveRecord::Base
  has_many :comments,:dependent=>:destroy,:as=>:commentable
  has_one :last_comment,
          :class_name=>"Comment",
          :order=>"id desc",
          :as=>:commentable
end

class Image < ActiveRecord::Base
  has_many :comments,:as=>:commentable
end

class Comment < ActiveRecord::Base
  belongs_to :commentable,:polymorphic=>true
end



即Comment是Post和Image的嵌套资源,要添加一个Post的Comment和Image的Comment的URL该怎么设计呢,Action又要怎么写?这种类型用传统风格很好实现,但REST?
   
0 请登录后投票
论坛首页 Ruby版 rails

跳转论坛:
JavaEye推荐