`
mypages
  • 浏览: 88010 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

应用Rails进行REST开发(六)

阅读更多

自定义 Action

我们已经知道,在 /config/routes.rb 里定义的路由,会自动生成对资源的CRUD的操作。但是我们如何处理那些并不是CRUD的操作?下面我们就用一个例子来说明这一点。例如我们在 ProjectController里有一个close的方法。这个close并不是真正的删除一个资源,而只是把给这个资源设置一个标志:表示这个
资源被关闭了。

首先修改一下数据库:
> ruby script/generate migration add_closed_to_projects
exists db/migrate
create db/migrate/003_add_closed_to_projects.rb
Listing 1.16: ontrack/db/migrate/003 add closed to projects.rb
class AddClosedToProjects < ActiveRecord::Migration
def self.up
add_column :projects, :closed, :boolean, :default => false
end
def self.down
remove_column :projects, :closed
end
end
rake db:migrate


现在,我们在IteratinController的index.rhtml上创建一个 close 的链接。


Listing 1.17: ontrack/app/views/projects/index.rhtml
<% for project in @projects %>
<tr id="project_<%= project.id %>">
<td><%=h project.name %></td>
<td><%= link_to "Show", project_path(project) %></td>
...
<td><%= link_to "Close", <WHICH_HELPER?> %></td>
</tr>
<% end %>


现在有2个问题摆在我们面前:
1.使用 http 协议的哪个动作来发送这个请求呢?
2.对于这个链接,该如何生成那些 helper方法呢?


因为这个 close 动作并不是CRUD中的任何一个,所以Rails 也不知道该用http的哪个来做这个事情。不过既然 close 也是 update 中的一种,所以应该使用post来发送这个请求。我们还是得在 /config/routes.rb 里定义这个路由,当然定义完路由之后,就会有相应的path和url的helper方法了。因为这个close的操作,仍然是针对projects 这个资源的,所以,我们可以在定义路由的时候,使用一个名字叫“member”的hashmap,这个hashmap 的key,就是自定义action的名字,hashmap的value,就是所使用的http的动作。

map.resources :projects, :member => { :close => :post }


hashmap 的value可以使用 :get, :put, :post, :delete, :any。如果使用了:any,那么可以用http的任何动作来发送这个请求。


定义完这个路由后,我们就可以使用helper方法了:
<td><%= link_to "Close", close_project_path(project) %></td>


因为我们定义的是“:member => { :close => :post }”,所以,这个请求只能以post的方式来发送,如果使用其它方式如“get”,那么请求就是无效的。为了安全起见,我们还是把它改成用按钮的方式来发 送,幸运的是我们可以使用Rails 提供的button_to 来做这件事情:


<td><%= button_to "Close", close_project_path(project) %></td>
=>
<td>
<form method="post" action="/projects/1;close" class="button-to">
<div><input type="submit" value="Close" /></div>
</form>
</td>


现在我们要做的就是写完 ProjectController中的 close 方法:
Listing 1.18: ontrack/app/controllers/projects controller.rb
def close
respond_to do |format|
if Project.find(params[:id]).update_attribute(:closed, true)
flash[:notice] = "Project was successfully closed."
format.html { redirect_to projects_path }
format.xml { head :ok }
else
flash[:notice] = "Error while closing project."
format.html { redirect_to projects_path }
format.xml { head 500 }
end
end
end


除了“:member”,我们还可以使用“:collection”,“:new”。
“:collection”的用途是:所操作的资源不是一个,而是很多个。下面是一个用“:collection”方式得到一个资源的列表的例子:
map.resources :projects, :collection => { :rss => :get }
--> GET /projects;rss (maps onto the #rss action)


所以,有的时候,“:member”更多的是更新一个资源,而“:collection”是得到一堆资源。
对于“:new”,一般用于那些还没有被保存的资源:
map.resources :projects, :new => { :validate => :post }
--> POST /projects/new;validate (maps onto the #validate action)

我们是否仍然“DRY”(Don’t Repeat Yourself)?

我们是否为了“DRY”原则?似乎是这样的:我们不仅在controller里定义了action,同时在 /config/routes.rb 里也定义了一遍。


作为替换REST风格的调用的方式,您可以用传统的方式来调用一个方法:
<%= link_to "Close", :action => "close", :id => project %>


但是别忘了,即使用传统的方式,你也得在/config/routes.rb里定义一个路由:“map.connect ’:controller/:action/:id’”。

自定义信息格式

目前 respond_to 可以返回如下的信息格式:
respond_to do |wants|
wants.text
wants.html
wants.js
wants.ics
wants.xml
wants.rss
wants.atom
wants.yaml
end


你可以通过增加新的MIME类型的信息来扩展这个功能。假设您已经开发了一个“PIM”应用系统,现在你希望把地址信息用”vcard” 格式来传送。要实现这一共能,首先你需要注册新的信息格式在
/config/environment.rb。
Mime::Type.register "application/vcard", :vcard然后,我们来修改一下show action,使得返回的信息以vcard 的格式来传送。


def show
@address = Address.find(params[:id])
respond_to do |format|
format.vcard { render :xml => @address.to_vcard }
...
end
end


这个 to_vcard 方法不是 ActiveRecord 的标准方法,所以必须按照 vcard的标准来实现(RFC2426)。如果实现正确的话,那么通过下面的URL,就可以得到正确的信 息:“http://localhost:3000/addresses/1.vcard”。

在REST 中使用AJAX

在REST风格的系统中使用AJAX?非常简单,可以说这一小节没什么新鲜的玩意要学习。您还是使用以前所使用的 remote 系列的helper 方法,只不过传递的参数需要改变,现在使用 path helper 方法,而不是以前所使用的 contoller,action 。下面的例子会让您更清晰的明白这一点:


link_to_remote "Destroy", :url => project_path(project),
:method => :delete
=>
<a href="#" onclick="new Ajax.Request("/projects/1",
{asynchronous:true, evalScripts:true, method:"delete"});
return false;">Async Destroy</a>


给您提醒一下:千万千万记得导入相应的ajax javascript 文件,不然当您的ajax无效,而气得把键盘砸坏的时候,我们就无能为力了。导入相应的javascript 文件相当的简单:


Listing 1.19: ontrack/app/views/layouts/projects.rhtml
<head>
<%= javascript_include_tag :defaults %>
...
</head>


这个“Destroy”链接将会调用ProjectsController的destroy方法。从逻辑上来说现在一切正常:用户点这个链接,系统删除相 应的资源。不过我们还是漏了一点:在 respond_to 中,我们应该增加新的返回类新,也就是javascript类型。

Listing 1.20: ontrack/app/controllers/projects controller.rb
def destroy
@project = Project.find(params[:id])
@project.destroy
respond_to do |format|
format.html { redirect_to projects_url }
format.js # default template destroy.rjs
format.xml { head :ok }
end
end


可以看出来,唯一的改变就是增加了“format.js”。因为这个“format.js”并不是一个要被执行的代码块,所以,Rails 会按照标准显示destroy.rjs。


Listing 1.21: ontrack/app/views/projects/destroy.rjs
page.remove "project_#{@project.id }"


这个 rjs 文件从当前的浏览页面中删除了 “project_ID”这个DOM元素,为了让这个删除起到效果,我们就需要在显示 project 上进行修改:
Listing 1.22: ontrack/app/views/projects/index.rhtml
...
<% for project in @projects %>
<tr id="project_<%= project.id %>">


这是一个遵循DRY原则和减少对当前系统的修改的一个好例子!也体现了REST的优势,只需要在controller里增加一行,就可以处理javascript请求了。

同时也告诉了我们一个REST编程的原则:在 respond_to 外实现逻辑处理,能够极大地降低重复的代码。

测试

不管开发REST风格的应用是多么的让我们激动,我们也不能忘记最重要的一个朋友:测试!
之前我们写了那么多代码,但是一次单元测试测试都没运行过!下面我们来运行一下吧!
> rake
...
Started
EEEEEEE.......


好消息是,所有的单元测试和功能测试都可以运行。坏消息是,关于IterationsController的7个功能测试,全部失败!

如果测试用例抛出异常,那么很明显的—这里存在一些错误。我们遇到的错误也很明显:所有的IterationsController的测试用例都是 scaffold 来生成的,并没有一个“父”资源的关联—还记得吗,我们已经让iterations 资源成为了projects 资源的“子”资源。


为了让我们的测试用例通过,我们必须给每个测试方法都增加project_id。例如:
Listing 1.23: ontrack/test/functional/iterations controller test.rb
def test_should_get_edit
get :edit, :id => 1, :project_id => projects(:one)
assert_response :success
end


当然了,你需要加载必要的fixtures:
fixtures :iterations, :projects


改完全部的测试用例以后,我们发现还是有2个测试用例无法通过:

test_should_create_iteration
test_should_update_iteration

失败的代码来自这行“assert_redirected_to iteration_path(assigns(:iteration))”。

错误是非常显然的:我们已经知道iteration_path的第一个参数应该是project id。我们同样需要修改一下:
assert_redirected_to iteration_path(projects(:one), assigns(:iteration))


另外,在使用 redirect 断言的时候,path helper 方法的使用,是REST和非REST风格应用的唯一区别。

REST 风格的客户端:ActiveResource

我们总是把 ActiveResource 和 REST一起提及。ActiveResource 是一个Rails 的库,用来开发基于REST的WEB服务客户端。这种基于REST的客户端,也是适用 http 的4个标准的动作来和服务器通信。


ActiveResource 并不是Rails 1.2 的一部分,但是您可以使用svn 从网站下载它的代码:
> cd ontrack/vendor
> mv rails rails-1.2
> svn co http://dev.rubyonrails.org/svn/rails/trunk rails


ActiveResource把客户端资源抽象成一个类,这个类继承自ActiveResource::Base。例如通过下面的例子,我们来调用服务器上的project 资源:
require "activeresource/lib/active_resource"
class Project < ActiveResource::Base
self.site = "http://localhost:3000 "
end


可以看到,我们导入了ActiveResource 的库,然后,服务器的地址赋给了类的变量 site。这个 Project 类,把服务器上的资源抽象成了一个客户端的类,这就让开发人员觉得他们就好像操作一个ActiveRecord 一样。

例如,我们用一个project id 和 find 方法去请求服务器上的一个资源:
wunderloop = Project.find 1
puts wunderloop.name


这个 find 方法会执行标准的GET动作:GET /projects/1.xml


然后服务器返回xml格式的信息。客户端把xml信息转化成一个ActiveResource对象 wunderloop,就好像一个ActiveRecord对象,可以得到和改变它的任何属性。那么我们如何去更新一个资源呢?

wunderloop.name = "Wunderloop Connect"
wunderloop.save


save 方法会是用put 动作向服务器传递信息。
PUT /projects/1.xml


刷新一下你的浏览器看看,那条记录肯定被改变了。和 find,save一样简单,创建一个新的资源也是非常方便:
bellybutton = Project.new(:name => "Bellybutton")
bellybutton.save


新的资源将会以post方式传递给服务器,并且保存到数据库里。
POST /projects.xml


刷新浏览器,会看到新建立的资源。最后,我们来看一下删除一个资源。
bellybutton.destroy


destroy方法将会以DELETE方式发送给服务器,并且删除这个资源。
DELETE /projects/2.xml


ActiveResource 使用http的4个动作来和服务器交互。对于REST的资源,它提供了非常好的抽象,此外,在ActiveRecord中的许多方法,在ActiveResource中仍然找得到。例如查找一个资源的全部记录:
Project.find(:all).each do |p|
puts p.name
end


相信使用ActiveResource可以开发出很好的松耦合的系统,我们不如马上去下载ActiveResource的代码,亲自体验一下吧!

大结局

这个世界并不是非得需要REST。有很多的解决方案可以考虑,并且可以很容易实现。大概更多的时候,是您可能现在正处于某个项目的中期,这时,您发 现了Rails这个新特性。我想如果您此时就开发一个单独的模块,并且使用REST的风格,是毫无问题的。如果您要准备开始一个全新的项目,那么不妨考虑 一下使用
REST,理由十分明显:清晰的架构,更少的代码,多个客户端的支持。

终于结束了,有关rails2.0 rest风格的新特性建议看一篇文章叫"Rolling with Rails 2.0 - The First Full Tutorial "。http://www.iteye.com/topic/162536

分享到:
评论

相关推荐

    应用Rails进行REST开发

    应用Rails进行REST开发.pdf Restful Rails Development

    Web开发敏捷之道-应用Rails进行敏捷Web开发(第3版).pdf

    《Web开发敏捷之道:应用Rails进行敏捷Web开发(第3版)》:Ruby on Rails是一个全套的MVC web框架,它能帮你开发高质量又美观的web应用,而且开发速度快得出乎你想象。你只须集中精力于应用程序本身,Rails就会帮你...

    RestFul_Rails_Dev_pdf_v_0.1.zip

    应用Rails进行REST 开发 ,翻译自《RESTful Rails Development》

    Web开发敏捷之道-应用Rails进行敏捷Web开发-第三版.rar

    相比第2版中的内容,Rails 2增加了REST、资源、轻量级web service等新特性。本书涵盖了这些全新的内容,因此能更好地体现出Rails框架的发展现状。 整体而言,全书既有直观的实例,又有深入的分析,同时还涵盖了web...

    跨越边界:REST on Rails

    随着Ruby的经验不断成功,开发人员开始寻求把他们的Ruby应用程序与用其他语言编写的应用程序集成。Rails对Web服务提供了优秀的支持。本文介绍Rails中的Web服务,重点放在一个名为Representational State Transfer ...

    Rails的精简版本Rails::API.zip

    Rails::API 是 Rails 的精简版本,针对不需要使用完整...也可以用来编写在 Web 应用和客户端之间进行数据共享的后端程序,允许开发者创建接受 JSON 格式数据并以传统 RoR 应用方式存储的REST端点。 标签:rails

    rails_5_api_tutorial:使用Swagger UI构建Perfect Rails 5 API Only应用程序并记录基于Rails的REST API

    构建Perfect Rails 5 API Only应用得益于作为Rails 5核心一部分提供的新的rails-api gem,Rails现在是快速,轻松构建精简API的理想选择。 到目前为止,可以说,在Ruby中创建API的最佳选择是Grape,尽管Grape仍然是一...

    Struts2-rest插件(有注释)

    Rails 开发者只需要按约定开发 ActiveRecord、ActiveController 即可,无需进行配置。很明显,Struts 2 的 Convention 插件借鉴了 Rails 的创意,甚至连插件的名称都借鉴了“约定优于配置”原则。 由于 Struts 2 的...

    RESTful Rails Development

    使用ROR进行REST风格的网络应用开发的教程。

    rails-api-base:Rails 5 RESTful API模板

    一个讨论Rails设置和开发的api示例。 从其开始项目的基本模板。 规格摘要: RESTful API。 api版本。 Notes应用示例。 模式和良好做法。 用户管理。 版本到期。 国际化。 秘密api密钥。 Rspec测试。 ...

    codecrux.github.io:Ruby on Rails,移动开发和设计公司

    我们的服务包括网络和移动应用程序设计与开发电子商务应用开发医疗保健应用开发房地产应用开发REST API开发IT基础架构管理和开发运营,后端服务器端编程和云服务(如Amazon,Heroku,IBM,Google,Microsoft

    rest-api-authentication-jwt-ruby:使用Ruby,Rails,PostgreSQL,JWT等开发的软件(REST-API)

    REST API身份验证Rails-JWT V1.0描述该存储库是具有Rails,JWT,PostgreSQL等的应用程序软件安装使用Ruby 2.7时,最好使用Rails 6.0。软件Ruby版本管理器RVM 1.29.10-next数据库使用PostgreSQL应用客户休息:邮递员...

    roar-rails:在Rails中使用Roar的代表

    使您在Rails应用程序中使用Roar的代表变得有趣。 咆哮轨道的开发将在未来停止,我们鼓励用户开始迁移到 (和 )。 Roar是用于解析和呈现REST文档的框架。 有关代表的更好的概述,请检查。 Roar-rails可为您提供...

    crewcloud-dashboard:包装 OpenStack REST API 的 Rails 应用程序,为学生提供简化的访问

    这是一个 Rails 应用程序,所以只是 git clone https://github.com/crew/crewcloud-dashboard cd crewcloud-dashboard bundle install rails s open http://localhost:3000 我们建议在开发时使用 Sublime Text 3 或...

    angular-rails-spa-demo:使用Ruby on Rails后端提供的JSON API的AngularJS SPA Web应用程序演示

    Ruby on Rails的REST API AngularJS客户端 PostgreSQL 使用rspec,水豚和Selenium进行测试 开发设置 rake db:create rake db:migrate rails server 测验 API请求规范 rake spec:requests 整合测试 rake spec:...

    ASP EXCEL导入SQL

    从最直观的角度看待REST,它是网络服务最理想的手段,但是Rails框架把REST带到了网络应用软件开发框架。这是一次飞跃,让REST的思想从网络服务的应用提升到了网络应用软件开发。利用REST思想的simply_restful插件...

    在Ruby on Rails中使用Rails Active Resource的教程

    简介 当今的应用程序不仅需要和基于浏览器的客户端互操作,还需要和...带有基于 Representational State Transfer (REST) 服务的 Rails 框架有一种更简单、更快捷的方法通过 Active Resource 来实现和使用 web 服务。

    RESTFUL WEB SERVICES中文高清版

    Oriented Architecture,ROA)、REST式设计的优点、REST式Web服务的真实案例分析、如何用各种流行的编程语言编写Web服务客户端、如何用三种流行的框架(Ruby on Rails、Restlet和Django)实现REST式服务等。...

    使用Struts2开发RESTful服务

    本文内容包括:REST简介资源和标识符操作资源的方式Struts2的REST支持RestActionMapper简介为Struts2应用安装REST插件实现支持REST的Action类实现视图层参考资料从V2.1开始,Struts2开始提供Convention插件,它允许...

Global site tag (gtag.js) - Google Analytics