跨越边界 系列中以前的文章说 Ruby on Rails 是一个突然流行起来的框架,充当着 Ruby 编程语言的催化剂。随着 Ruby 的经验不断成功,开发人员开始寻求把他们的 Ruby 应用程序与用其他语言编写的应用程序集成。Rails 对 Web 服务提供了优秀的支持。本文介绍 Rails 中的 Web 服务,重点放在一个名为 Representational State Transfer (REST) 的策略上。

过去的 20 年间,一个趋势主导了商业软件工具的开发:用复杂性对抗复杂性。这一趋势在任何地方都没有比在分布式计算领域更明显。C 和 Java™ 社区已经看到一些惊人复杂的框架被构建出来支持分布式通信。分布式计算环境(DCE)支持用 C 语言编写的应用程序之间的远程过程调用。公共对象请求代理架构(CORBA)标准支持面向对象应用程序之间的通信。企业 JavaBean(EJB)规范提供安全性、持久性、事务、消息和远程的服务。对各个框架的宣传甚嚣尘上,但是这些框架都没有满足预期,有些甚至因为它们的复杂性而成为灾难。在这些框架中,只有 EJB 3.0 属于大力简化的结果,有潜力在分布式应用程序上成功。市场可能给、也可能不给这个面临强敌的框架另一个空间,但 EJB 仍然需要交付使用。
最新的大型分布式框架是 Web 服务。Web 服务技术让应用程序可以用平台独立或编程语言独立的方式相互通信(请参阅  参考资料)。Web 服务标准也受到复杂性恶魔的威胁,但是称作 REST 的替代策略承诺了更简单的方式。本文介绍了如何在 Ruby on Rails 中添加 REST 风格的 Web 服务,并从 Ruby 和 Java 代码调用服务。
关于本系列

在  跨越边界 系列中,作者 Bruce Tate 推动了这样一个概念:今天的 Java 程序员通过学习其他方法和语言,可以受益。编程阵营中 Java 技术是所有开发项目最佳选择的情况已经变了。其他框架正在改变 Java 框架构建的方式,从其他语言学到的概念有助于 Java 编程。编写的 Python(或 Ruby、Smalltalk 或 ……)代码可以改变 Java 编码的方式。
本系列介绍了与 Java 开发有根本不同,但是却直接适用的编程概念和技术。在某些情况下,需要集成这些技术以利用它。在其他情况下,将可以直接应用这些概念。单独的工具不如其他语言和框架中影响 Java 社区的开发人员、框架甚至基本方法的思想重要。
Web 服务领域
就像 EJB、CORBA 和 DCE 一样,Web 服务的核心抽象也是远程过程调用。Web 服务利用叫做 SOAP(最初,SOAP 代表简单对象存取协议,但是这个术语现在降级了)的协议,用 XML 表示消息的结构。这里有一个技巧:如果协议用代表简单的  S 开始,那它就不简单。Web 服务定义语言(WSDL)提供了服务的标准规范。像 SOAP 一样,WSDL 也是一个棘手而复杂的 API,而 SOAP 和 WSDL 仅仅涉及到了构成 Web 服务这个大怪物的众多 API 的表面(请参阅  参考资料)。Web 服务需要一次大修,感谢 Roy Fielding 的一份有影响的博士论文,Web 服务得到了大修(请参阅  参考资料)。
Fielding 的论文描述了 REST 应用程序联网策略。REST 与全堆栈 Web 服务根本不同,主要原因有三个:
  • REST 的核心抽象是远程资源而不是远程过程调用。
  • REST 没有发明一个详尽的标准列表,而是采用现有的 Internet 标准,包括 HTTP、XML 和 TCP/IP。
  • REST 没有覆盖每个可能场景,而是覆盖了最常见的问题。
请把 REST 想像成浏览。REST 客户使用与浏览器相同的 HTTP 命令访问资源。当 REST 客户访问到资源的表示时,客户转换到一个状态。使用不同的 HTTP 命令,REST 客户可以创建、读取、更新或删除资源的记录。
例如,以典型的博客为例。通过输入 URL,例如 blog.rapidred.com,得到贴子的列表。然后,如果想编辑博客条目,可以在 URL 中输入 HTTP 参数(例如 blog.rapidred.com/edit?article=12345),然后显示编辑表单。由于每个博客条目都有自己的 URL,所以点击链接或直接输入 URL,就可以用 HTTP 命令读取、修改或删除内容。
简而言之,REST 可以:
  • 用 TCP/IP 命名标准命名 Web 上的资源
  • 用 HTTP 查询和操纵这些资源
  • 使用基于文本的标准消息格式(例如 XML 或 HTML)来构造数据
Ruby on Rails 用 REST 对 Web 服务提供了优秀的支持。

回页首
Action Web Services 概述
Rails 用叫做 Action Web Services 的模块实现 Web 服务。许多开发框架鼓励视图和 Web 服务使用独立的控制器。这个策略可以维护控制器之间的风格一致。问题是针对所服务的每种内容,都需要一个新控制器。例如,Ajax 用户界面要求从控制器取得到 JavaScript 的远程 XML 调用。
不必为 Web 服务专门分配一个控制器,使用 Rails,可以通用地用同一个控制器向基于 HTML 的视图、基于 XML 的 Web 服务和基于 XML 的 JavaScript 组件提供内容。理解 Action Web Services 的最好方式就是在工作应用程序的环境下查看它的实际作用。
请用自己选择的数据库管理器创建一个叫做  service_development 的数据库。接下来,用以下命令创建 Rails 项目和模型:
> rails service
> script/generate model Person
在生成模型之后,就有了一个叫做 db/migrate/001_create_people.rb 的迁移。请把这个迁移编辑成像清单 1 一样:

清单 1. people 表的迁移

class CreatePeople < ActiveRecord::Migrationdef self.upcreate_table :people do |t|t.column :first_name, :string, :limit => 40t.column :last_name, :string, :limit => 40t.column :email, :string, :limit => 40t.column :phone, :string, :limit => 15endenddef self.downdrop_table :peopleend
end
把 config/database.yml 中的数据库配置修改成与自己的数据库配置匹配,并输入  rake migrate。最后,输入  script/generate scaffold Person People,为  Person 模型和  People 控制器生成工作台。现在可以用  script/server 启动服务器了。请把浏览器指向 localhost:3000/people,以看到针对  Person 的经典的 Rails 脚手架。图 1 显示了带有标准 Rails 脚手架的应用程序:

图 1. 简单的 Rails 应用程序

在我介绍 Rails 的 Web 服务之前,请查看控制器代码。编辑 app/controllers/people_controller.rb,使之与清单 2 的代码匹配:

清单 2. PeopleController 的控制器代码

class PeopleController < ApplicationControllerdef indexlistrender :action => 'list'end# GETs should be safe (see
[url]http://www.w3.org/2001/tag/doc/whenToUseGet.html[/url])verify :method => :post, :only => [ :destroy, :create, :update
],:redirect_to => { :action => :list }def list@person_pages, @people = paginate :people, :per_page => 10enddef show@person = Person.find(params[:id])enddef new@person = Person.newenddef create@person = Person.new(params[:person])if @person.saveflash[:notice] = 'Person was successfully created.'redirect_to :action => 'list'elserender :action => 'new'endenddef edit@person = Person.find(params[:id])enddef update@person = Person.find(params[:id])if @person.update_attributes(params[:person])flash[:notice] = 'Person was successfully updated.'redirect_to :action => 'show', :id => @personelserender :action => 'edit'endenddef destroyPerson.find(params[:id])。destroyredirect_to :action => 'list'end
end
如果跟着做过这个系列以前的 Ruby on Rails 项目,就会知道典型的控制器方法的一般流程是:
  1. 用户通过跟随链接或指定 URL,通过 HTTP 发送请求。
  2. Web 服务器根据域的配置把请求转给 Ruby on Rails。
  3. Rails 路由器根据 URL 模式把请求路由给控制器。默认模式是 http://主机名/控制器/动作/参数
  4. 路由器用与动作相同的参数调用控制器上的方法。
  5. 动作参数为视图设置实例变量,并呈现视图。
  6. 动作方法把实例变量拷贝到视图。
例如,请看  清单 2 中的  show 方法。控制器设置视图使用的  @person 实例变量。因为方法没有指定视图的名称,所以 Rails 用与控制器动作相同的名称调用视图 —— 在这个示例中,视图位于 app/views/people/show.rhtml。
再来看  list 方法。如果想让这个方法呈现 XML,需要:
  • 删除分页
  • 把 people 实例变量转换成 XML
  • 呈现 XML 而不是 HTML
Rails 使得处理 Web 服务和呈现来自同一 Web 服务的视图成为可能。实际上也不需要分页。为了把 Web 服务的  list方法简化一些,可以把控制器中的  list 方法变成像清单 3 一样,清除分页。还需要删除靠近 app/views/people/list.rhtml 代码底部的 “Next Page” 和 “Previous Page” 链接。

清单 3. 简化 list

def list@people = Person.find_all
end
由于删除了分页,也就删除了让用户界面更健壮的一个特性,但是又得到了一些回报。可以用相同的代码来驱动 Web 服务和视图。如果日后发现需要分页,可以编写一些定制的助手。
现在基本应用程序出来了,可以添加一些 Web 服务了。

回页首
向 Rails 控制器添加 Web 服务
如果我想说大话,我可以说 “现在已经有了一个 Web 服务”。记得我对 REST 说过什么?这种风格的 Web 服务使用指定的资源。我的 Rails 应用程序也具有指定的资源:host_name/people/list 调用我的  list 服务。REST 风格的 Web 服务也使用 TCP/IP 和 HTTP。我的 Rails 应用程序就是这么做的。而且格式良好的 HTML 就是 XML 的子集,也满足最后一条 REST 要求。只需在 localhost:3000/people/list 上调用 HTTP  get,并解析结果,就可以得到人员列表。这就是关键。REST 的工作方式与 Internet 的工作方式一样。但这并不是真正基于 REST 的 Web 服务。理想情况下,应当提供反映  Person 含义的 XML 文档而不是用户界面的结构。
真正的服务应当产生纯数据的表示,一个专门针对服务的预期客户而构建的表示。但是示例应用程序有两个客户:终端用户和 REST 客户。要为两个目的重用相同的代码,需要给 Rails 提供更多信息。Rails 的设计者可能决定使用额外的 URL 参数,但是处理 URL 可是一项费劲的工作。Rails 不应当用这些细节增加用户负担。相反,HTTP 提供了指定更多信息的工具:HTTP 头。
要理解 Web 服务的 REST 模型,了解一点 HTTP 是有帮助的。 curl(请把它想像成  查看 URL)命令允许用一个命令查询 URL,并查看响应。基于 Unix 的操作系统默认包含  curl,可以为其他操作系统下载免费的  curl 工具。通过输入  curl http://some-url,可以将请求限制成只输出默认的响应体(浏览器呈现的 HTML)。输入  curl -i http://some-url 可以得到更多信息。这个命令返回 HTTP 头,如清单 4 所示。可以看到头配置由表示每个请求的配置的键-值对组成。

清单 4. 用 curl 调用 HTTP 请求

> curl -i [url]http://localhost:3000/people/list[/url]
HTTP/1.1 200 OK
Cache-Control: no-cache
Connection: Keep-Alive
Date: Tue, 27 Jun 2006 14:54:49 GMT
Content-Type: text/html; charset=UTF-8
Server: WEBrick/1.3.1 (Ruby/1.8.4/2005-12-24)
Content-Length: 854
Set-Cookie: _session_id=216912045de52786f032b22755c903dd; path=/
后面将频繁地看到 HTTP  getputpost 和  delete 命令。REST 利用达些命令执行经典的 CRUD(CRUD 是create, read、update 和 delete 的共同缩写)。HTTP 命令到 CRUD 的映射是这样的:
  • Create(创建):HTTP put
  • Read(读取):HTTP get
  • Update(更新):HTTP post
  • Delete(删除):HTTP delete
浏览器利用 HTTP 头,通过相同的服务器端代码来满足不同类型的请求。行为良好的应用程序提供正确处理文档的充足信息。其中一条信息叫做 HTTP  Accept 头。只要多花一点力气,控制器就能利用一些助手,用  Accept 头决定如何响应进入的请求。然后,控制器可以呈现适当的响应。请把  PeopleController 中的  list 方法改成像清单 5 一样:

清单 5. 扩展 list方法以呈现 XML

def list# wants is determined by the http Accept header in the request@people = Person.find_allrespond_to do |wants|wants.htmlwants.xml { render :xml => @people.to_xml }end
end
在清单 5 中,可以看到完整的基于 REST 的 Web 服务。生成的代码是 Rails 中小型的特定于域的语句的优美示例,它扩展 Ruby 以构造一种 switch 语句。它的工作方式是这样的:
  1. respond_to 方法接受单个代码块,并传递一个实例变量(标为 wants)到代码块。
  2. wants 对每个可能的类型都有一个方法。控制器可以为控制器期望的每个类型指定一个代码块。
  3. 如果方法名称与 HTTP Accept 头中的类型匹配,wants 方法执行对应的代码块。
  4. 如果没有指定代码块(例如 wants.html),Rails 就执行默认动作(在这个示例中,呈现 app/views/people/list.rhtml)。
这个策略允许在所有预期的客户之间共享相同的设置代码。如果需要添加期望 HTML 的 JavaScript 客户,以便让应用程序支持 Ajax,只需要添加 wants.js,如清单 6 所示:

清单 6. 为 JavaScript 客户呈现 HTML

def list# wants is determined by the http Accept header in the request@people = Person.find_allrespond_to do |wants|wants.htmlwants.jswants.xml { render :xml => @people.to_xml }end
end
现在已经看到了如何向只读的方法中添加 REST Web 服务。 show 方法也类似,如清单 7 所示:

清单 7. 实现 show

def show@person = Person.find(params[:id])respond_to do |wants|wants.htmlwants.xml { render :xml => @person.to_xml }end
end
您可能已经注意到,通过 REST 看到的只有只读服务。原因是:让应用程序处理提交和删除所需要的工作比较少。删除不需要额外的支持,因为当前的代码已经用 URL 指定了要删除的人的 ID。Rails 自动转换  post 请求中进入的 XML,所以不需要构建任何服务器端支持。实际上,应用程序不用变就能删除、更新和创建。可以修补每个方法呈现的 HTTP 响应,但是客户代码实际就在 HTTP 返回码之后。
现在是调用 Web 服务的时候了。

回页首
调用 Web 服务
使用现有 HTTP 协议这一策略使得调用变得简单。清单 8 显示了 Ruby 版本。请注意 HTTP  Accept头。记住,控制器根据这个头决定内容的类型。

清单 8. 从 Ruby 调用服务

require 'net/http'
Net::HTTP.start('localhost', 3000) do |http|response = http.get('/people/list', 'Accept' => 'text/xml')#Do something with the response.puts "Code: #{response.code}" puts "Message: #{response.message}"puts "Body:\n #{response.body}"
end
清单 8 中的 Web 服务调用,在 [url]http://localhost:3000/people/list[/url] 上调用 HTTP  get 方法,并输出响应。Ruby 有很好的库可以处理生成的 XML,但是它们超出了本文的范围。不需要用 Ruby 调用这个服务。只需要 HTTP 的库。清单 9 显示这个服务的 Java 调用:

清单 9. 用 Java 代码调用服务

package com.rapidred.ws;
import java.net.*;
import java.io.*;
public class SimpleGet {void get() {try {URL url = new URL("http://localhost:3000/people/list");URLConnection urlConnection = url.openConnection();urlConnection.setRequestProperty("accept", "text/xml");BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));String str;while ((str = in.readLine()) != null) {System.out.println(str);}in.close();}catch (Exception e) {System.out.println(e);}}
像其 Ruby 等价物一样,这个代码打开一个 URL 连接,把  Accept 头设置成  text/xml,发出  get,并输出结果。Java 代码有许多 XML 框架(请参阅  参考资料),但是我在这个示例中硬编码了 XML,以保持示例简单。
post 的调用也相似。清单 10 显示了简单的  post

清单 10. 用 Java 代码调用 HTTP post

void post() {
try {String xmlText  = "<person> " +"<first-name>Maggie</first-name>" +"<last-name>Maggie</last-name>" +"<email>[email]maggie@tate.com[/email]</email>" +"</person>";URL url = new URL("http://localhost:3000/people/create");HttpURLConnection conn = (HttpURLConnection)url.openConnection();conn.setDoOutput(true);conn.setRequestMethod("POST");conn.setRequestProperty("Content-Type", "text/xml");OutputStreamWriter wr = new
OutputStreamWriter(conn.getOutputStream());wr.write(xmlText);wr.flush();BufferedReader rd = new BufferedReader(new
InputStreamReader(conn.getInputStream()));String line;while ((line = rd.readLine()) != null) {System.out.println(line);}wr.close();rd.close();} catch (Exception e) {System.out.println("Error" + e);}
}
这个 HTTP  post 通过在 [url]http://localhost:3000/people/create[/url] 上调用  post,并在 HTTP 文档体中传递一个 XML 文档,创建了一个新  Person。(通常应当用 Java XML 库构建 XML 文档。这次我还是硬编码了 XML 文档,以保持示例简单。)Rails 支持自动把进入的 XML 转换成  Person 属性的 Ruby 散列表。

回页首
结束语
在本文中,已经看到只用少量代码,就使控制器支持基于 REST 的 Web 服务。动态类型化的 Internet 语句,例如 Ruby,大量地利用 REST 代替基于 SOAP 的 Web 服务。一些简单的调用,包括漂亮的  responds_to 语法和对进入提交的自动 XML 转换,使得可以容易地利用同一控制器处理 Web 服务、远程 JavaScript 请求或 HTML。
Java 语言对 REST 也有非常好的支持。毕竟,servlet 实际上是服务器端基于 REST 的 Web 服务。可以在 Java 端使用 servlet,在 Ruby 端使用 Rails 控制器,把利用两个平台优势的应用程序组合在一起。这就是 Web 服务的漂亮之处。您真正需要的所有东西就是超群出众的勇气。
参考资料

学习

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
  • Beyond Java(Bruce Tate,O'Reilly,2005):作者的一本关于 Java 语言的兴起、平台期以及其他能够在某些领域挑战 Java 语言的技术的书。
  • Standards and Web services:Web 服务标准的复杂集合。
  • Architectural Styles and the Design of Network-based Software Architectures(Roy Thomas Fielding,Irvine 的加州大学,2000):Fielding 的博士论文描述了 REST。
  • “面向资源与面向活动的 Web 服务”(James Snell,developerWorks,2004 年 10 月):迅速查看一下 REST 风格的 Web 服务和 SOAP 风格的 Web 服务之间的关系。
  • SOA and Web services 新手入门:请参阅这份 developerWorks 针对新 Web 服务程序员的资源指南。
  • “Web services, Rails style”:Jamis Buck 关于 Rails Web 服务的简短博客入口。
  • From Java To Ruby: Things Your Manager Should Know(Pragmatic Bookshelf,2006):作者的书,介绍了什么时候和在哪里从 Java 编程转向 Ruby on Rails 才有意义,以及如何进行转换。
  • Programming Ruby(Pragmatic Bookshelf,2005):关于 Ruby 编程的流行书籍。
  • “Book review: Agile Web Development with Rails”(Darren Torpey,developerWorks,2005 年 5 月):深入研究这本书可以加深读者对 Rails 和敏捷开发方法背后的逻辑的理解。
本文转自 fsjoy1983 51CTO博客,原文链接:http://blog.51cto.com/fsjoy/76919,如需转载请自行联系原作者

REST on rails相关推荐

  1. 诗歌rails之如何写一个简单的Rails Plugin

    生成plugin骨架代码: Ruby代码 ruby script\generate plugin MyPlugin ruby script\generate plugin MyPlugin 功能需求: ...

  2. 我的Rails笔记(1)

    <Agile Web Development With Rails>Notebook. 环境: Rails 3.1.0 Gem 1.8.10 Ruby ruby 1.9.2p180 1. ...

  3. [rails] 我的订餐系统 -- 小试ruby on rails(转)

    前言         近期在java社区中一种新的脚本语言ruby,及用ruby开发的一个wab框架 rails也热闹了起来.引起了不少的java开发人员的关注. 本人平时还是很少接触脚本语言方面东东 ...

  4. rails 添加外键_如何在Rails后端中添加功能强大的搜索引擎

    rails 添加外键 by Domenico Angilletta 通过多梅尼科·安吉列塔(Domenico Angilletta) In my experience as a Ruby on Rai ...

  5. ruby on rails_我成为了Ruby on Rails和React的贡献者,你也可以

    ruby on rails I am really grateful to have contributed to a few open source projects, including two ...

  6. 新手安装ruby on rails(ror)的成功必备手册

    2019独角兽企业重金招聘Python工程师标准>>> 如何快速正确的安装 Ruby, Rails 运行环境 每一位使用windows系统来进行ROR开发项目的都是这个世界上折翼的天 ...

  7. rails应用ajax之二:使用rails自身支持

    考虑另一种情况: 1. 页面上半部分显示当前的所有用户,页面下半部分是输入新用户的界面: 2. 每当输入新用户时,页面上半部分会动态更新新加用户的内容: 我们还是用ajax实现,不过这次用rails内 ...

  8. 例题6-2 铁轨(Rails, ACM/ICPC CERC 1997, UVa 514)

    栈应用 例题6-2 铁轨(Rails, ACM/ICPC CERC 1997, UVa 514) 错解 1.每次要把栈清空 2.不能用空的栈(栈顶)去比较 #include<iostream&g ...

  9. nginx rails 详细站点配置入门教程

    Ruby on Rails 是一个用于开发数据库驱动的网络应用程序的完整框架.Rails基于MVC(模型- 视图- 控制器)设计模式.从视图中的Ajax应用,到控制器中的访问请求和反馈,到封装数据库的 ...

  10. Rails 添加新的运行环境

    Rails自带了development.test和production三个environments 我们可以添加Staging database.yml staging: adapter: mysql ...

最新文章

  1. C语言打印1000以内的完数
  2. Python小知识点(5)--面向对象部分
  3. QT的QBarCategoryAxis类的使用
  4. PCB设计的基本步骤
  5. python菜单栏_pyqt5——菜单和工具栏
  6. HTML small元素
  7. 《现代操作系统》笔记 2 线程
  8. 【CCCC】L2-028 秀恩爱分得快 (25分),模拟题
  9. [渝粤教育] 广东-国家-开放大学 21秋期末考试互联网营销概论10092k2
  10. Win10 输入法工具栏抽风,无法调整水平垂直。
  11. matlab调整文字方向,python 旋转文字方向_如何通过Python 在绘图中旋转文字?
  12. 【高阶乐理】即兴演奏——和弦进行的重要原则(现代流行乐)
  13. python日期间隔天数_Python编程题5--计算两个日期之间相隔的天数
  14. KDD2015,Accepted Papers
  15. 我的世界服务器成就系统的其他成就是什么,大量的成就奖励亟待领取!《我的世界》【PC版】Hypixel服务器成就系统来啦~...
  16. 论文阅读八:SDN 交换机转发规则 TCAM 存储优化综述
  17. 树莓派-防火墙规则设置
  18. mysql 表别名_MySQL 表别名(Alias)
  19. 物理机安装linux系统,U盘在物理机安装linux系统
  20. JavaWeb网上订餐管理系统|餐饮就餐订餐餐厅(含源码+论文+答辩PPT等)

热门文章

  1. 使用SuspendLayout和ResumeLayout
  2. c语言扬声器程序,通过扬声器C编程并播放声音
  3. (二)目标检测之 HOG+SVM 算法
  4. 赶快更新LastPass
  5. Windows 包管理器 - Scoop
  6. PP图和QQ图以及它们意义
  7. 服务器系统装内存条,hp服务器怎么安装内存条 hp服务器内存推荐【图文】
  8. 液基细胞学加计算机阅片,细胞学实验室 - foliagetx博客 - 华夏病理网博客
  9. 手机寿命不到三年,为什么现在的手机越来越短命?
  10. 三个绘图工具类详解Paint(画笔)Canvas(画布)Path(路径)