写点什么

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

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

关注

评论 1 条评论

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

阿里P8面试官让我吃透这份10W字Java面试题,终于拿下Java高级岗Offer

做梦都在改BUG

Java java面试 Java八股文 Java面试题 Java面试八股文

基于IM的企业移动应用平台,支持企业定制化

WorkPlus

面对向多模态发展的趋势,为什么这些业界和学界专家说“不必追热点”

小红书技术REDtech

深度学习 专家 活动回顾

SpringCloud 网关实现线程池异步批量保存请求日志

Java你猿哥

spring Spring Cloud Java工程师 日志表

Kubernetes 中容器跨主机网络是怎么样的?

做梦都在改BUG

Java Kubernetes 云原生

Github最新开源!Alibaba 亿级并发系统架构(2023全彩版小册)

Java你猿哥

Java 数据库 缓存 分布式 高并发

聊聊简单又不简单的图上多跳过滤查询

华为云开发者联盟

大数据 后端 华为云 华为云开发者联盟 企业号 4 月 PK 榜

安全测试前置实践2-安全渗透测试

京东科技开发者

测试 安全测试 功能测试 网络安全渗透测试 企业号 4 月 PK 榜

分享:CUDB for OceanBase分布式数据库产品规模应用

OceanBase 数据库

数据库 oceanbase

java性能优化实战:高并发系统的法宝之缓存设计

Java你猿哥

高并发 缓存并发 缓存设计 Java高并发 Java性能优化

什么是接口定义? 接口定义的概念和用途详解

Apifox

前端 接口 后端 API 接口定义

技术不行还说Java卷!靠468页SpringBoot企业级项目实战成功逆袭

做梦都在改BUG

Java 微服务 Spring Boot 框架

投放视频广告时,如何快速与第三方播放器兼容?

HarmonyOS SDK

HMS Core

Redis源码之SDS简单动态字符串

Java你猿哥

Java redis ssm Java工程师

AI与打工人:相互补充,共同进步 | 社区征文

海拥(haiyong.site)

三周年征文

ChatGPT背后的AI背景、技术门道和商业应用(万字长文,建议收藏)

京东科技开发者

人工智能 AI ChatGPT 人工智能ChatGPT 吗? 企业号 4 月 PK 榜

京东技术专家首推:微服务架构深度解析,GitHub星标120K

Java你猿哥

数据库 架构 微服务 ssm Java微服务

SpringCloud 网关实现线程池异步批量保存请求日志

做梦都在改BUG

Java Spring Cloud 网关设计

【机器学习入门与实践】数据挖掘-二手车价格交易预测(含EDA探索、特征工程、特征优化、模型融合等)

汀丶人工智能

人工智能 数据挖掘 机器学习 深度学习 模型融合

防治“虚假种草”,小红书技术团队干了这几件大事

小红书技术REDtech

架构 AI 小红书

横扫一线大厂面试的高并发笔记到底有多硬核?

小小怪下士

Java 程序员 后端 高并发 java面试

使用Python实现一个简单的垃圾邮件分类器

海拥(haiyong.site)

三周年连更

数据解析NFT Q1市场表现:NFT生态正向Polygon聚拢,蓝筹项目"保值"难

NFT Research

数据分析 NFT

硬核!互联网资深大佬手码高并发编程速成笔记(2023版)限时开源

Java你猿哥

性能优化 系统架构 ssm 高并发 Java高并发

人工智能基础数据服务,第一!

百度开发者中心

人工智能 数据标注 元宇宙

关于Blender你想了解的都在这里

Finovy Cloud

blender 3D软件

跟随项曙明走进中兴通讯,探索企业开源风险治理优秀实践

开源雨林

开源治理 中兴通讯

3000字13张图详细介绍RAID0、1、5、6、10、50、60,非常值得收藏!

wljslmz

raid 存储技术 三周年连更

私有化视频会议系统, WorkPlus Meet助力企业 “面对面”安全开会!

WorkPlus

数据库原理及MySQL应用 | 日志管理

TiAmo

数据库 MySQL数据库 日志管理 三周年连更

ONES × 中国信通院《中国企业软件研发管理白皮书》即将发布

万事ONES

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