立即领取|华润集团、宁德核电、东风岚图等 20+ 标杆企业数字化人才培养实践案例 了解详情
写点什么

用 ROR 创建面向资源的服务

  • 2008-06-09
  • 本文字数:4170 字

    阅读完需:约 14 分钟

Ruby on Rails(ROR)的成功主要在于它的简单化假设(simplifying assumptions)。Rails 并不是向你提供一大堆用以解决各种问题的工具,而是为你提供了一种用以解决各类常见问题的方式。可以非常讯速地创建 Rails 应用,只要:尝试把关系数据库中的数据暴露出来,数据库表有确定的名称与结构,你愿意采用一种模型 - 视图 - 控制器(Model-View- Controller)架构,等等。因为在 Web 应用领域中,很多问题均符合这些假设,所以问题一般都能顺利解决,很少会遇到麻烦。

Rails 过去的版本暴露的是经典的 REST-RPC 混合服务,不过自 Rails 1.2 版起,它开始更加注重 REST 式设计了。或许这是必然的:HTTP 统一接口(uniform interface)是另一个简单化假设(simplifying assumptions)。我在第 7 章已经向你展示过 Rails 框架是如何做到用少量代码实现复杂的 REST 式服务了,在这一节,我将对此做一个回顾,并 概括性地讲述 Rails 的 REST 式架构。

路由

当 Rails 收到一个 HTTP 请求时,它会根据该请求的目标 URI,把请求路由给适当的控制器类(controller class)处理。如示例 12-1 所示,config/routes.rb 文件定义了 Rails 应如何对给定请求进行处理。

示例 12-1:一个简单的 routes.rb 文件

# routes.rb<p>ActionController::Routing::Routes.draw do |map|</p><p> map.resources :weblogs do |weblog|</p><p> weblog.resources :entries</p><p> end</p><p>end</p>config/routes.rb 文件可以相当复杂。第 7 章中展示的那个(见示例 7-3)相对来说算复杂的:我有很多资源,必须克服简单化假设,以得到想要的 URI 结构。示例 12-1 是一个较简单的、接纳了简单化假设的 routes.rb 文件。

通过该文件可知有两个控制器类(WeblogsController 和 EntriesController),该文件告诉 Rails 如何把收到的请 求路由给这两个类。WeblogsController 类处理发给 /weblogs 及 /weblogs/{id} 的请求。这里的路径变量{id}是放在 params[:id] 里的。

EntriesController 类处理发给 /weblogs/{weblog_id}/entries 及 /weblogs /{weblog_id}/entries/{id}的请求。这里的路径变量{weblog_id}是放在 params[:weblog_id] 里的, {id}是放在 params[:id] 里的。

{id}、{weblog_id}等变量常用于进行资源(resource)与系统里特定对象(object)的关联。它们常常跟数据库里的记录 ID 相对应,并经常被代入 ActiveRecord 的 find 方法。在我的社会性书签服务(见第 7 章)里,试过给它们像{username}这样具备描述 性的名称,并用它们来标识名称而不是 ID。

资源、控制器和视图

正如我在第 7 章中所讲述的那样,每个 Rails 控制器可以暴露两种资源。你可以有一个“列表”或“工厂”资源(响应 GET 和 / 或 POST 请求)和许 多“对象”资源(响应 GET、PUT 和 DELETE 请求)。一般来说,列表资源跟数据库表相对应,而对象资源跟数据库表里的记录相对应。

每个控制器都是一个 Ruby 类;于是,向一个类“发送”HTTP 请求,就意味着调用某个特定的方法。Rails 为每个控制器定义了五个标准方法,并 通过 HTTP GET 暴露了两个特殊的视图模板。在示例 12-1 中调用的 map.resources :weblogs 使得下面这七种 HTTP 请求成为可能。

  • GET/weblogs:一个博客列表。 Rails 调用 WeblogsController#indexmethod。
  • GET/weblogs/new:用于新建一个博客的表单。Rails 用 app/view/weblogs/new.rhtml 来呈现该视图。该视图是一个超媒体文件,它描述了客户端应发送什么样的 HTTP 请求来创建一个博客。
    换句话说,这是一个 HTML 表单(其实也可以是一个简短的 WADL 文档)。这个表单指出,客户端应向 /weblogs(见下)发送 POST 请求来新建博 客。它还指出客户端应为这个新博客采用什么样的表示格式(representation format),以便服务器能理解它。
  • POST/weblogs:创建一个新博客。Rails 调用 WeblogsController#create 方法。
  • GET/weblogs/{id}:一个博客。Rails 调用 WeblogsController#show 方法。
  • GET/weblogs/{id};edit:修改博客状态的表单。Rails 用 app/view/weblogs/edit.rhtml 来呈现该视图。该视图是一个超媒体文件,它描述了客户端应发送什么样的 HTTP 请求来修改博客的状态。
    该视图可以是一个 HTML 表单或一个简短的 WADL 文档。它告诉客户端应如何向 /weblogs/{id} 发送 PUT 请求。
  • PUT/weblogs/{id}:修改博客的状态。Rails 调用 WeblogsController#update 方法。在这里的“状态”指的是与博客资源关联的状态(state),如博客的名称及作者联系信息等。各篇博客文章是作为单独资源暴露出来的。
  • DELETE /weblogs/{id}:删除一个博客。Rails 调用 WeblogsController#delete 方法。

你也许不会为创建的所有控制器都暴露这七个接口。特别是,多半不会使用那些特殊视图,除非打算把 Web 服务作为一个 Web 网站来运营——没问题,你只要别实现那些不打算暴露的方法或视图就行了。

返回的表示

Rails 使我们能够更容易实现“根据客户端的请求,返回一个资源的不同表示”。示例 12-2 所示的 Ruby 代码可以返回一个博客的三种不同表示。 它将根据客户端请求的目标 URI 或 Accept 报头决定返回哪个表示。若客户端向 /weblogs/1.html 发出请求,就会得到 HTML 版表示;若客 户端向 /weblogs/1.png 发出请求,就会得到 PNG 版表示。respond_to 函数负责解释客户端的能力与需求。你所需要做的,就是按优先级 实现被支持的选项。

示例 12-2:返回多个表示中的一个

respond_to do |format|<p> format.html { render :template => 'weblogs/show' }</p><p> format.xml { render :xml => weblog.to_xml }</p><p> format.png { render :text => weblog.generate_image,</p><p> :content_type => "image/png" }</p><p>end</p><br></br>HTML 和 ActiveResource XML 序列化格式(serialization format)是两种特别常见的表示格式(representation formats)。HTML 表示是用 Rails 视图来展现的(就像在面向人类用户的 Web 应用程序中一样)。要把一个 ActiveRecord 对象暴露为 一个 XML 文档,你只要调用该对象(或对象列表)的 to_xml 方法即可。

通过 Rails 插件,我们可以轻易地把具有其他表示格式的数据暴露出来。在第 7 章,我安装了 atom-tools Ruby gem,用以为书签列表生成 Atom 提要(feed)。示例 7-8 里有一个 respond_to 代码块,它用于根据请求决定返回 Atom 还是普通 XML 表 示。

收到的表示

Rails 的工作就是根据收到的表示(incoming representation)生成一组关键字 - 值对(key-value pairs),并以 params hash 的形式来提供这些关键字 - 值对。默认情况下,它知道如何解析 Web 浏览器发送的表单编码的(form-encoded)文档,以及 to_xml 生 成的 XML 文档。

如果希望它能够解析自己的表示格式,你可以在 ActionController::Base.param_ parsers hash 里添加一个新的 Proc 对象。该 Proc 对象是一段代码,它的作用是处理服务器收到的具有给定媒体类型的表示。关于 param_parsers hash 的详细情况,请参阅 Rails 文档。

将 Web 应用作为 Web 服务

Rails 1.2 在融合 human web 与 programmable web 方面做得相当好。正如我在第 3 章中展示的,Rails 自带一个叫做 scaffold_resource 的代码生成器,它可以把数据库表暴露为一组资 源。你可以用 Web 浏览器来访问这些资源,也可以用 Web 服务客户端(如 ActiveResource)来访问这些资源。

如果你用 Web 浏览器来访问 scaffold_resource 服务的话,你会得到数据库对象的 HTML 表示,以及用于操作它们的 HTML 表单(由 前面提到的 new.rhtml 和 edit.rhtml 生成)。你可以通过发送表单编码格式(form-encoded format)的表示来创建、修改或删除资源。PUT 和 DELETE 请求是通过重载的 POST(overloaded POST)来模拟的。

如果你用一个 Web 服务客户端来访问 scaffold_resource 服务的话,你会得到数据库对象的 XML 表示。你可以修改该 XML 文档,并通过 PUT 请求把它发回去。非重载的(non-overloaded)POST 与 DELETE 请求的工作方式与你预期的一样。

关于 programmable web 与 human web 的基本相似性,没有比这更能令人信服的例子了。第 7 章因为篇幅原因所以没有讲述 Rails 的这方面内容,不过它非常有说服力地证明了 Rails 适合 于“设计具有相同功能的网站和 Web 服务”的场合。Rails 可以用同一套底层代码来暴露网站和 Web 服务。

Rails/ROA 设计步骤

下面是一个根据第 6 章的通用设计步骤修改后得到的版本。我在第 7 章设计社会性书签服务时已经非正式地采用这些设计步骤了。这里的设计步骤跟第 6 章的 区别在于:你不是直接把数据集划分为一个个资源,而是把数据集划分为一个个控制器,再把控制器划分为资源。这样,就不会出现“你最后得到的资源不适应 Rails 控制器”的问题了。

  1. 规划数据集。
  2. 把数据集分配给一个个控制器。
    对于每一个控制器:
  • 该控制器暴露的是一个列表或工厂资源吗?

  • 该控制器暴露的是一组对象资源吗?

  • 该控制器暴露的是一个用于创建或修改资源的表单资源吗?
    对于列表和对象资源:

    • 设计来自客户端的表示(假如与 Rails 标准不同的话)。
    • 设计返回给客户端的表示。
    • 把该资源与已有资源联系起来。
    • 考虑有哪些典型的事件经过?第 9 章描述的“基于数据库的应用的控制流”在此会有帮助。
    • 考虑可能出现哪些错误情况?同样,这里常常可以采用基于数据库的应用的控制流。

本文节选自博文视点出版公司即将推出的经典著作《RESTful Web Services 中文版》中的第 12 章《REST 式服务框架》。

《RESTful Web Services 中文版》向 读者介绍了什么是 REST、什么是面向资源的架构(Resource-Oriented Architecture,ROA)、REST 式设计的优点、REST 式 Web 服务的真实案例分析、如何用各种流行的编程语言编写 Web 服务客户端、如何 用三种流行的框架(Ruby on Rails、Restlet 和 Django)实现 REST 式服务等。不仅讲解 REST 与面向资源的架构(ROA)的概念与原理,还向读者介绍如何编写符合 REST 风格的 Web 2.0 应用。本书详实、易懂,实战性强,提供了大量 RESTful Web 服务开发的最佳实践和指导,适合广大的 Web 开发人员、Web 架构师及对 Web 开发或 Web 架构感兴趣的广大技术人员与学生阅读。

与此同时,博文视点还授权 InfoQ 中文站独家为大家提供额外的样章进行试读:欢迎下载第 3 章《REST 式服务有什么不同》

相关阅读:****

2008-06-09 20:322535
用户头像

发布了 63 篇内容, 共 26.0 次阅读, 收获喜欢 11 次。

关注

评论 1 条评论

发布
暂无评论
发现更多内容

开发一个交易所需要多少钱?多少时间?

dappweb

数字揭秘丨如何设计出坚不可摧的网球拍?

Altair RapidMiner

人工智能 数字孪生 仿真 altair

护航端侧大模型平稳健康发展,百度大模型内容安全Lite版正式发布

百度安全

从传统到智能,“小浣熊家族AI办公助手”引领数据处理的革命

完美句号

办公 办公平台 AI Agent 效率办公 小浣熊家族

内网通讯软件有哪些?

WorkPlus

蓝易云 - C#中使用IntPtr.Size属性来判断当前系统是32位还是64位

百度搜索:蓝易云

C# 云计算 运维 云服务器 服务器租用

移动门户平台有哪些?安全专属的移动应用管理平台WorkPlus为企业保驾护航

WorkPlus

区块链交易所是什么?

dappweb

538个代码示例!麻省理工教授的Python程序设计+人工智能案例实践

我再BUG界嘎嘎乱杀

Python 人工智能 编程 程序设计 开发语言

职场<火焰杯>测试开发大赛决赛即将开始!

霍格沃兹测试开发学社

氛围共处(Ambient Co-presence)丨RTE 共读计划

声网

蓝易云 - jsp和servlet有什么区别?

百度搜索:蓝易云

Java Linux 运维 Web jsp

区块链交易所是什么?

区块链开发团队DappNetWork

开发一个交易所需要多少钱?多少时间?

区块链开发团队DappNetWork

深度报告 | 百度安全携手极越安全发布《整车安全渗透测试白皮书》

百度安全

Golang channel底层是如何实现的?(深度好文)

golang架构师k哥

golang

蓝易云 - Linux系统中“sid”是什么意思?

百度搜索:蓝易云

云计算 Linux 运维 云服务器 服务器租用

蓝易云 - GIT日常命令收集

百度搜索:蓝易云

git 云计算 云服务器 服务器租用 高防服务器

区块链交易所是怎么开发的?

区块链开发团队DappNetWork

百度智能云:千帆大模型平台升级十大能力,企业级RAG全面升级

IT新闻资讯

合合信息大模型“加速器”亮相2024世界人工智能大会,助力大模型学好“专业课”

合合技术团队

人工智能 合合信息 文档解析

科研菜鸡的好助手——代码小浣熊+办公小浣熊

卟噜卟噜叭

蓝易云 - 学习aop以及nginx

百度搜索:蓝易云

nginx Linux 运维 aop 云服务器

im即时通讯哪家好?WorkPlus im即时通讯集成底座为企业保驾护航

WorkPlus

im即时通讯软件有哪些?WorkPlus安全专属移动数字化平台

WorkPlus

适用于 Rust 新手的编程练习项目

Leon Ding

新手指南 ​Rust rust语言 新手

用ROR创建面向资源的服务_Ruby_徐涵_InfoQ精选文章