论坛首页 编程语言技术论坛

也谈一下我对Rails 1.2中 Restful的理解

浏览 20851 次
该帖已经被评为精华帖
作者 正文
   发表时间:2006-12-15  
讨论:

我总觉得REST应该用于提供数据而不是页面,还是用用户和圈子作例子吧,感觉顺畅一些。

比如某个用户的页面,他选一个圈子,点击“加入”就加入了这个圈子;而圈子的管理员也可以选择用户,并点击“添加”把用户加入圈子。

这里2个页面完成的是同一个操作,都是创建一个用户-圈子的关系,但操作完以后的页面转向会不同。如果用REST来提供页面,如何来处理这些转向?另加参数?那不是搞复杂了嘛。

所以我觉得应该是浏览器上通过AJAX请求这个action,返回数据,浏览器脚本把数据处理成显示,这也是AJAX最适合的方式。

如果只提供数据,实际上不需要7种action,4种就可以了:
GET /posts    => /posts/index
GET /posts/1  => /posts/index/1
PUT /posts/1  => /posts/update/1
POST /posts   => /posts/create
DELETE /posts/1 => /posts/destroy/1

new和edit都是提供页面的。
0 请登录后投票
   发表时间:2006-12-15  
qiezi 写道
讨论:
所以我觉得应该是浏览器上通过AJAX请求这个action,返回数据,浏览器脚本把数据处理成显示,这也是AJAX最适合的方式。

new和edit都是提供页面的。


我进来在想restful形式的分页的时候就觉得,不符合restful的页面应该以AJAX方式呈现。
0 请登录后投票
   发表时间:2006-12-15  
qiezi 写道
讨论:

我总觉得REST应该用于提供数据而不是页面,还是用用户和圈子作例子吧,感觉顺畅一些。

比如某个用户的页面,他选一个圈子,点击“加入”就加入了这个圈子;而圈子的管理员也可以选择用户,并点击“添加”把用户加入圈子。

这里2个页面完成的是同一个操作,都是创建一个用户-圈子的关系,但操作完以后的页面转向会不同。如果用REST来提供页面,如何来处理这些转向?另加参数?那不是搞复杂了嘛。

所以我觉得应该是浏览器上通过AJAX请求这个action,返回数据,浏览器脚本把数据处理成显示,这也是AJAX最适合的方式。

如果只提供数据,实际上不需要7种action,4种就可以了:
GET /posts    => /posts/index
GET /posts/1  => /posts/index/1
PUT /posts/1  => /posts/update/1
POST /posts   => /posts/create
DELETE /posts/1 => /posts/destroy/1

new和edit都是提供页面的。


根据session里面的用户来决定,管理员和普通用户的页面跳转当然不一样。
0 请登录后投票
   发表时间:2006-12-16  
最近一直在补Ruby,心里惦记着Rails1.2但是一直没有时间去细看。昨天看了DHH的"Discovering a world of Resources on Rails",再来看楼主这个发帖,发现楼主对这个例子本身的理解有些不完整,对RESTful的理解也有偏差。

楼主的例子看似和DHH的例子很像,但实际很不同。DHH的例子是User/Group,典型的use case是“管理员把某个用户添加到某个组”。这样的例子,从Model到Controller到View恰好是完整的一套,很清晰也很优美。这里User是操作的对象。

而User/Article的例子中,这个User是操作的对象,同时又是操作的主体。一个可能是use case是“用户标记某文章为‘已读’”,但更典型的use case是“当用户阅读某篇文章的同时,标记‘已读’”。这里面同时有两个操作:1)用户阅读文章 2)标记‘已读’(或者说删除‘未读’)。这个例子比User/Group的例子要复杂得多。

其实在DHH的讲演里面提到了这样的例子(恰恰是说“例外”的时候)。我想要在此引用一下DHH的一句话:
引用

CRUD is not a goal,it’s an aspiration,a design technique
CRUD不是一个目标,而是一种精神,一种设计技巧


这句话之后DHH就举了个不用CRUD的例子:Kase。Kase的例子是说,当用户close一个Kase的时候,同时建立一个Closure来保存谁、什么时间关闭了Kase。
当然,这个例子和楼主的例子也是有区别的。楼主的例子里面,阅读一篇文章并不修改文章本身,而关闭一个Kase要修改Kase本身;但是共同点是,都同时涉及到两个Controller。DHH把这种情况当作aspect/view来看:
POST /kases/1;close
POST /identity;aspect

DHH的例子是:
class KasesController < ActionController::Base
  # POST /kases/1;close
  def close
    @kase.close!
    redirect_to :action=>"show"
  end
end

class ClosuresController < ActionController::Base
  def create
    Closure.create!(:kase=>@kase, :closer=>@user
    redirect_to(kase_url(@kase))
  end
end


目前我还不知道Rails1.2中怎样同时用两个Controller。另外我很感兴趣的是,如果这两个Controller做的事需要是transactional的,即或者都成功或者都不成功,rails有没有这样的支持(在一个Controller中当然没有问题,但分散到两个Controller的话rails还能实现transactional吗)。从这个角度来看,我甚至严重怀疑DHH的这个例子是否恰当!

从楼主的代码看,只支持“用户标记某篇文章为‘已读’”,而不能支持用户阅读的同时标记,所以我说这不是个好例子。

看完了DHH那个presentation,我以为Rails1.2的RESTful最闪亮的点是通过ActiveResource提供了漂亮的、简单的系统间(或者系统的不同部分之间)的交互。RESTful绝不仅仅是CRUD,CRUD只是达到RESTful的手段而已。在一般情况下,CRUD精神能简化设计,但CRUD不应得到过分的强调,更不是RESTful的精髓。KasesController::close已经告诉我们"CRUD is everything"这种想法是错误的。
另外,如果在一个系统内部为了RESTful而RESTful,并不能获得RESTful的好处却带来设计及实现上的麻烦,也是不可取的。
0 请登录后投票
   发表时间:2006-12-16  
aardvark 写道
最近一直在补Ruby,心里惦记着Rails1.2但是一直没有时间去细看。昨天看了DHH的"Discovering a world of Resources on Rails",再来看楼主这个发帖,发现楼主对这个例子本身的理解有些不完整,对RESTful的理解也有偏差。

楼主的例子看似和DHH的例子很像,但实际很不同。DHH的例子是User/Group,典型的use case是“管理员把某个用户添加到某个组”。这样的例子,从Model到Controller到View恰好是完整的一套,很清晰也很优美。这里User是操作的对象。

而User/Article的例子中,这个User是操作的对象,同时又是操作的主体。一个可能是use case是“用户标记某文章为‘已读’”,但更典型的use case是“当用户阅读某篇文章的同时,标记‘已读’”。这里面同时有两个操作:1)用户阅读文章 2)标记‘已读’(或者说删除‘未读’)。这个例子比User/Group的例子要复杂得多。

其实在DHH的讲演里面提到了这样的例子(恰恰是说“例外”的时候)。我想要在此引用一下DHH的一句话:
引用

CRUD is not a goal,it’s an aspiration,a design technique
CRUD不是一个目标,而是一种精神,一种设计技巧


这句话之后DHH就举了个不用CRUD的例子:Kase。Kase的例子是说,当用户close一个Kase的时候,同时建立一个Closure来保存谁、什么时间关闭了Kase。
当然,这个例子和楼主的例子也是有区别的。楼主的例子里面,阅读一篇文章并不修改文章本身,而关闭一个Kase要修改Kase本身;但是共同点是,都同时涉及到两个Controller。DHH把这种情况当作aspect/view来看:
POST /kases/1;close
POST /identity;aspect

DHH的例子是:
class KasesController < ActionController::Base
  # POST /kases/1;close
  def close
    @kase.close!
    redirect_to :action=>"show"
  end
end

class ClosuresController < ActionController::Base
  def create
    Closure.create!(:kase=>@kase, :closer=>@user
    redirect_to(kase_url(@kase))
  end
end


目前我还不知道Rails1.2中怎样同时用两个Controller。另外我很感兴趣的是,如果这两个Controller做的事需要是transactional的,即或者都成功或者都不成功,rails有没有这样的支持(在一个Controller中当然没有问题,但分散到两个Controller的话rails还能实现transactional吗)。从这个角度来看,我甚至严重怀疑DHH的这个例子是否恰当!

从楼主的代码看,只支持“用户标记某篇文章为‘已读’”,而不能支持用户阅读的同时标记,所以我说这不是个好例子。

看完了DHH那个presentation,我以为Rails1.2的RESTful最闪亮的点是通过ActiveResource提供了漂亮的、简单的系统间(或者系统的不同部分之间)的交互。RESTful绝不仅仅是CRUD,CRUD只是达到RESTful的手段而已。在一般情况下,CRUD精神能简化设计,但CRUD不应得到过分的强调,更不是RESTful的精髓。KasesController::close已经告诉我们"CRUD is everything"这种想法是错误的。
另外,如果在一个系统内部为了RESTful而RESTful,并不能获得RESTful的好处却带来设计及实现上的麻烦,也是不可取的。

谢楼上的思考,我也把"Discovering a world of Resources on Rails"重看了一遍,我认为楼主的做法是正确的,只是他没说清楚一些定义而已。

RESTful核心就是资源,把关系也看作是资源,所以用户加入和退出组可以改成用户与组的关系这个资源的创建和删除。基于这一点我觉得RESTful应该翻译为REST风格,不给它个合适的中文词汇来代替,总是理解起来有些偏差。这一点楼主的例子也是合适的。

你说的Kase那部分实际上DHH提到了3种做法(技巧)[注],而不是说KasesController#close和 ClosuresController#create要分别调用,既然创建了Closure,它里面就包含created_at,这和kase里的 closed_at不是同一个意思吗?何必还更新kase对象呢?所以根本没有事务要考虑,你“甚至严重怀疑DHH的这个例子是否恰当”,我觉得是没理解这个部分。

注:这3种做法,第1种是我们现在用的,scaffold产生的代码就是这种。第2种是把关系转成资源来创建。第3种是用has_one。这3种分别对应到他前面所说的事件、关系、状态。
0 请登录后投票
   发表时间:2006-12-16  
qiezi 写道

谢楼上的思考,我也把"Discovering a world of Resources on Rails"重看了一遍,我认为楼主的做法是正确的,只是他没说清楚一些定义而已。

RESTful核心就是资源,把关系也看作是资源,所以用户加入和退出组可以改成用户与组的关系这个资源的创建和删除。基于这一点我觉得RESTful应该翻译为REST风格,不给它个合适的中文词汇来代替,总是理解起来有些偏差。这一点楼主的例子也是合适的。

你说的Kase那部分实际上DHH提到了3种做法(技巧)[注],而不是说KasesController#close和 ClosuresController#create要分别调用,既然创建了Closure,它里面就包含created_at,这和kase里的 closed_at不是同一个意思吗?何必还更新kase对象呢?所以根本没有事务要考虑,你“甚至严重怀疑DHH的这个例子是否恰当”,我觉得是没理解这个部分。

注:这3种做法,第1种是我们现在用的,scaffold产生的代码就是这种。第2种是把关系转成资源来创建。第3种是用has_one。这3种分别对应到他前面所说的事件、关系、状态。


我对DHH怎么做那一部分确实不太理解,但你的解释就让我更疑惑了。从他的代码看,closed_at确实是在kases表中,而在closures表中有没有created_at却并不明确。另外,kases表中还有个closed要更新:
class Kase < ActiveRecord::Base
  def close!(at = Time.now)
    self.closed_at = at
    self.closed = true
    save!
  end
end

这怎么都是两条SQL的事,怎么可能没有事务要考虑呢?虽然说ActiveRecord有built-in transaction,但好像不适用这种情况吧。
0 请登录后投票
   发表时间:2006-12-16  
aardvark 写道

我对DHH怎么做那一部分确实不太理解,但你的解释就让我更疑惑了。从他的代码看,closed_at确实是在kases表中,而在closures表中有没有created_at却并不明确。另外,kases表中还有个closed要更新:
class Kase < ActiveRecord::Base
  def close!(at = Time.now)
    self.closed_at = at
    self.closed = true
    save!
  end
end

这怎么都是两条SQL的事,怎么可能没有事务要考虑呢?虽然说ActiveRecord有built-in transaction,但好像不适用这种情况吧。

那是3种不同的做法:

第一种是修改记录,同时修改closed和closed_at。

第二种是创建一个Closure对象,是不是有created_at无关紧要,如果你需要closed_at就加上created_at来代替它,不需要说明。是否已经closed可以通过kase的关联来查询到,所以不需要更新kase,否则用这种关联方式岂不是降低效率还自讨苦吃?

第三种是多次创建Progress,但只有最后一个是有效的,用不同的记录来显示状态。

这三种也就是他说的“技巧”,之间没有关联,如果一件事还要请求2次,当然是不合理的。
0 请登录后投票
   发表时间:2006-12-16  
qiezi 写道
aardvark 写道

我对DHH怎么做那一部分确实不太理解,但你的解释就让我更疑惑了。从他的代码看,closed_at确实是在kases表中,而在closures表中有没有created_at却并不明确。另外,kases表中还有个closed要更新:
class Kase < ActiveRecord::Base
  def close!(at = Time.now)
    self.closed_at = at
    self.closed = true
    save!
  end
end

这怎么都是两条SQL的事,怎么可能没有事务要考虑呢?虽然说ActiveRecord有built-in transaction,但好像不适用这种情况吧。

那是3种不同的做法:

第一种是修改记录,同时修改closed和closed_at。

第二种是创建一个Closure对象,是不是有created_at无关紧要,如果你需要closed_at就加上created_at来代替它,不需要说明。是否已经closed可以通过kase的关联来查询到,所以不需要更新kase,否则用这种关联方式岂不是降低效率还自讨苦吃?

第三种是多次创建Progress,但只有最后一个是有效的,用不同的记录来显示状态。

这三种也就是他说的“技巧”,之间没有关联,如果一件事还要请求2次,当然是不合理的。


Please READ!
0 请登录后投票
   发表时间:2006-12-16  
aardvark 写道
qiezi 写道
aardvark 写道

我对DHH怎么做那一部分确实不太理解,但你的解释就让我更疑惑了。从他的代码看,closed_at确实是在kases表中,而在closures表中有没有created_at却并不明确。另外,kases表中还有个closed要更新:
class Kase < ActiveRecord::Base
  def close!(at = Time.now)
    self.closed_at = at
    self.closed = true
    save!
  end
end

这怎么都是两条SQL的事,怎么可能没有事务要考虑呢?虽然说ActiveRecord有built-in transaction,但好像不适用这种情况吧。

那是3种不同的做法:

第一种是修改记录,同时修改closed和closed_at。

第二种是创建一个Closure对象,是不是有created_at无关紧要,如果你需要closed_at就加上created_at来代替它,不需要说明。是否已经closed可以通过kase的关联来查询到,所以不需要更新kase,否则用这种关联方式岂不是降低效率还自讨苦吃?

第三种是多次创建Progress,但只有最后一个是有效的,用不同的记录来显示状态。

这三种也就是他说的“技巧”,之间没有关联,如果一件事还要请求2次,当然是不合理的。


Please READ!

Please READ!

我没看出来哪有2条SQL,既然都不用请求2次,难道是这里有2条SQL?
class Kase < ActiveRecord::Base
  def close!(at = Time.now)
    self.closed_at = at
    self.closed = true
    save!
  end
end

只有一个更新要什么事务?

我上面已经说了,我认为这3种之间毫无关联,如果你非要认为第1种和第2种有什么关联,那你解释一下第3种和前2种有什么关系吧,它们放在一起并没有什么不一样。
0 请登录后投票
   发表时间:2006-12-16  
1)先说David的代码
不想再paste一遍了,请看Kase::close!,这里难道不是要更新kases表中的一个row吗?
再看ClosuresController::create,这里难道不是要在closures中创建一个row吗?
这不是两条SQL是什么?

2)再说楼主的例子
假设说一个用户要阅读一篇文章,你是不是应该去调ArticlesController::show呢?那么Unreaded怎么得到更新?
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics