红帽白皮书新鲜出炉!点击获取,让你的云战略更胜一筹! 了解详情
写点什么

用 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:322282
用户头像

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

关注

评论 1 条评论

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

恭喜天翼云“翼起飞”战队在CCF国际AIOps挑战赛中夺得亚军!

天翼云开发者社区

呆猫云工作站助力Omniverse云上部署试水元宇宙

神奇视野

【小程序项目开发-- 京东商城】uni-app之自定义搜索组件(下) -- 搜索历史

计算机魔术师

8月月更

接口测试实战 | Android 高版本无法抓取 HTTPS,怎么办?

霍格沃兹测试开发学社

安全可信 | 首批!天翼云通过可信云安全云工作负载保护平台评估

天翼云开发者社区

技术分享 | 一文带你了解测试流程的体系

霍格沃兹测试开发学社

软件测试 | 测试开发 | 接口自动化测试中如何对xml 格式做断言验证?

测吧(北京)科技有限公司

接口自动化测试

代码层走进“百万级”分布式ID设计

得物技术

数据库 缓存 分布式 性能优化 企业号九月金秋榜

加快云网融合发展,打通算力传输大动脉!

天翼云开发者社区

【Django | 开发】面试招聘网站(增加csv,excel导出&企业域账号集成&日志管理功能)

计算机魔术师

8月月更

【Django | 开发】面试招聘信息网站(处理产品细节和权限&美化页面样式)

计算机魔术师

8月月更

测试开发基础 | Python 算法与数据结构面试题系列一(附答案)

霍格沃兹测试开发学社

测试管理和领导力秘诀,12+ BAT 大厂测试经理的干货经验汇总

霍格沃兹测试开发学社

《低代码发展白皮书(2022年)》&《2022低代码·无代码应用案例汇编》,发布了

华为云开发者联盟

云计算 后端 低代码 开发

软件测试 | 测试开发 | 接口自动化测试之JSON Schema模式改如何使用?

测吧(北京)科技有限公司

JSON Schema

测试面试 | 某 BAT 大厂测试开发面试真题与重点解析

霍格沃兹测试开发学社

技术分享 | 做为测试,那些必须掌握的测试技术体系

霍格沃兹测试开发学社

【Django | allauth】useprofile 用户模型扩展

计算机魔术师

8月月更

【Django | 开发】面试招聘信息网站(划分面试官权限&集成钉钉消息)

计算机魔术师

8月月更

技术分享 | 软件测试入门必会-流程管理平台

霍格沃兹测试开发学社

【Django | allauth】重写allauth重置密码方法

计算机魔术师

8月月更

OpenHarmony技术挑战课题征集

OpenHarmony开发者

OpenHarmony

教育信息化迈入2.0时代,呆猫云工作站破除技术壁垒

神奇视野

多线程原理和常用方法以及Thread和Runnable的区别

共饮一杯无

多线程 8月月更

MobTech 短信验证使用说明与应用创建

MobTech袤博科技

App sdk 短信验证

测试面试 | 一道大厂算法面试真题,你能答上来吗?(附答案)

霍格沃兹测试开发学社

驭数有道,天翼云TeleDB系列产品全新升级

天翼云开发者社区

“云”上交通,“翼”路畅行

天翼云开发者社区

看完这篇你将get VR/AR沉浸式技术的“创作密码”,速来!

神奇视野

【Django | 开发】面试招聘信息网站(快速搭建核心需求)

计算机魔术师

8月月更

leetcode 28. Implement strStr() 实现 strStr()(简单)

okokabcd

LeetCode 数据结构与算法

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