低代码到底是不是行业毒瘤?一线大厂怎么做的?戳此了解>>> 了解详情
写点什么

超越 XML 格式的多样化 REST 风格 API

2007 年 4 月 26 日

Rails 在 1.2 版中坚决地引入了 REST 风格的资源,在这个 REST 风格资源的世界中,XML 理所当然地成为了通用标准。不过这并没有不允许其它标准的存在,而多亏 Rails 的灵活性,REST 风格的应用能轻而易举地支持 XML 以外的标准,还能使这些应用面向更多的用户以及(或者)减少它们对带宽的需求。

建立 Controller

我打算将目光聚焦于一个应用,该应用仅处理事件这一个资源。应用将融合不同的标准来提供标准的一系列 CRUD 操作。开始前首先要创建一个新的 Rails 应用:

% Rails lingua<br></br> % cd lingua随后用 scafford_resource 生成器来创建一个事件资源:

% script/generate scaffold_resource Event打开并查看 app/controllers/events_controller.rb 文件,你将看到 respond_to 被大量用于为每个 action 添加 XML 输出,例如‘show’这个 action:

def show<br></br> @event = Event.find(params[:id])<p> respond_to do |format|</p><br></br> format.html # show.rhtml<br></br> format.xml { render :xml => @event.to_xml }<br></br> end<br></br> end(本文中我们不打算介绍授权和认证。但我强烈建议将 restful_authentication 插件作为学习授权和认证的入门)。

JSON 简介

JSON 是最近才走入人们视线的一个标准,这还要多亏 JavaScript 作为 UI 开发语言的成熟应用以及 AJAX 的迅猛发展。以序列化的 JavaScript 对象为基础的 JSON 获得了广泛认可,它被认为能以远比 XML 更好的方式来序列化和传输简单数据结构,而且它更简洁。

因为 ActiveRecord 早已能将它的记录以 JSON 格式持久化了,所以让 Rails 输出 JSON 格式显得易如反掌。如果我们想要的不过是 JSON 格式的输出,那么我们只需要对 action 的代码做如下修改:

def show<br></br> @event = Event.find(params[:id])<p> respond_to do |format|</p><br></br> format.html # show.rhtml<br></br> format.xml { render :xml => @event.to_xml }<br></br> format.json { render :text => @event.to_json }<br></br> end<br></br> end那么现在对 /events/1.json 的 GET 请求将获得以 JSON 格式返回的事件。

可若要实现真正的多种标准并存,我们还要理解(解析)JSON 而不是简单地介绍它。我们有许多方式可以做到这一点。首先我们需要能在 Ruby 中解析 JSON。

归根到底,合法的 JSON 格式也是合法的 YAML 格式。因此由于早期 Rails 对 YAML 的支持,最简单的解析器莫过于已经作为符号存在于 Rails 中的 YAML 解析器:

ActionController::Base.param_parsers[Mime::YAML] = :yaml不过 JSON 允许一些额外的结构(特别是注释),它们可能会被一些不那么小心的 JSON 编写者误输入了。那些现成的 JSON 解析器破坏了 Rails 的 JSON 生成过程,因为在提供解析和生成 JSON 方法的同时,它们重定义了 to_json 方法。我的解决方案是仅提取解析的功能,并将代码以 json.rb 文件的形式存储在我的 lib 文件夹下。代码太长所以不适合粘贴在这里,不过你可以在以下地址找到它:

http://jystewart.net/code/json/json.txt

一旦我们有了解析 JSON 的方式,我们接下来就需要将方法应用到 Rails 中。最直观的应用就是为我们的 controller 添加 before_filter,用它来截取 JSON 格式并进行解析,再将解析结果作为标准的参数 hash 提交给 action。我们可以这样去做:

before_filter :intercept_json, :if => Proc.new { |p| p.content_type == Mime::JSON }<p> def intercept_json</p><br></br> params[:event] = JSON::parse(params[:event])<br></br> end但如果我们打算添加其它资源,这就显得不那么 DRY 了(译者注:DRY——Don’t repeat yourself,不要重复你自己)。而如果我们添加了对更多格式的支持,我们将需要面对每个 controller 中的大量代码。Rails 将 XML 格式的输入透明地提交给 controller,而它或许也能够这样处理 JSON。谢天谢地,这的确可以!

Rails1.2 还引入了可插入的 param_parsers,它使我们能够定义如何处理每种 MIME 类型。事实上,XML 解析就是这样定义,代码如下:

ActionController::Base.param_parsers[Mime::XML] = :xml_simple标准的 Rails 参数解析过程(由 CGI 实现)捕获了解析过程中的任何异常,因此特定的解析器能够被用于任何 mime 类型。我们将自己的解析器添加到 environment.rb 中。要添加 JSON 解析器,我们需要做的是:

ActionController::Base.param_parsers[Mime::JSON] = Proc.new do |data|<br></br> JSON::parse(post)<br></br> end那么现在我们的参数 hash 将成为不可序列化的 JSON 请求。

这就是这个应用所做的一切。只要我们真的按照步骤构建了数据库(译者注:由于我们现在没有构建数据库,本文的示例还无法正常运行),我们的事件 controller 现在就能呈现为一个 REST 风格的资源,该资源既能以 XML 格式也能以 JSON 格式读写。而我们选择 JSON 可能是因为它简单到仅需用到 JavaScript,我们能够非常容易地将行为层从应用逻辑中分离出来,并使用 JSON 来衔接二者,又或者因为大部分主流编程语言都提供轻量级的 JSON 库,JSON 可以被作为一种低带宽占用的方式来接收 REST 风格 API 的输入。

Microformats 简介

Microformats 与 JSON 相似,它最近才越来越受人关注,但这些关注来源于一个与 JSON 迥然不同的角度。在 Web 开发的世界中,我们基于标准来使用 HTML 便能获得很好地支持,使 HTML 具有语义(在结构上使用 ID 和类名称)的目标似乎触手可及。简单地将右侧导航栏作为页面右侧最大的区域,或者甚至将它的 ID 标注为“右边栏”的做法已经过时了,现在我们可以在 Microformats 中将它标识为“二级导航”,然后开始关注内容而非呈现。

Microformat 尝试将常用元素的语义标准化,随之衍生了一系列新名词,如针对事件的 hCalendar(取材于 iCalendar 标准),针对个人和商务信息的 hCard(取材于 vcard),针对新闻的存储格式 hAtom(取材于 atom 联合格式)等。

添加 Microformat 输出甚至比生成 JSON 格式更简单,我们只需要确保将 class 名称约定应用到我们的.rhtml 视图文件中。解析它们则需要花一些脑筋,因为(以 HTML 的形式存在的)Microformats 没有一个特定的、被接受的 mime 类型来将它们与标准的请求(译者注:这里主要指 http 请求)区分开来。我待会继续讨论这个话题,不过首先我们来看看如果我们已经标识了 Microformats,应该如何来解析它的输入。

Ruby 开发者幸运地拥有了几个最灵活的 Microformat 解析库之一——Chris Wanstrath 编写的 Mofo。Mofo 基于 why 编写的 hpricot 库(译者注:hpricot 是 Ruby 编写的 HTML 解析库),它提供了描述 Microformats 的 DSL。它分发了类,这些类提供了大部分通用 Microformats 的定义。因此要将 HTML 字符串中的 hCalendar 事件解析并展示出来,我们需要做的是:

require 'mofo' data = <our data="" html=""></our> events = HCalendar.find(:all, :text => data)我们用下面的方式来读取所有 Microformats Mofo 了解的内容:

require 'mofo'<br></br> data = <our data="" html=""></our><br></br> parsed_data = Microformat.find(:all, :text => data)<br></br>随后 Mofo 将以易于访问的对象形式来返回所有 Microformat 数据。

Hpricot 和 Mofo 都是非常有用的库,不过仍旧无法依赖它们来解析我们所有的输入,特别是如果我们要考虑标准的 web 接口时。使用 URI 概要(作为 Web 接口)标识 Microformats 时会有一些不同,但对它们的支持离通用和稳定的目标还很远。但愿随着 Microformats 的成熟,有更多处理这类情况的规范出现。

就现在而言,最简单的应用莫过于尝试用标准的 CGI 解析器来解析输入,然后回去检查下 POST 请求的 body,看看它的内容是否是一个单独的字符串或独立的一些参数。我用到了 microformat_interceptor 这个通用的 Proc 对象(译者注:Proc 是 Ruby 中的过程对象):

microformat_interceptor = Proc.new do |data|<br></br> parsed = CGI.parse(data)<p> if parsed.collect { |key, value| key + '=' + value[0] }.first == data</p><br></br> Microformat.find(:all, :text => data)<br></br> else<br></br> CGIMethods.parse_request_parameters(parsed)<br></br> end<br></br> end随后为它分配 text/html 和 application/x-www-form-urlencoded 两种 mime 类型:

Mime::FORM = Mime::Type.lookup("application/x-www-form-urlencoded")<p> ActionController::Base.param_parsers[Mime::FORM] = microformat_interceptor</p><br></br> ActionController::Base.param_parsers[Mime::HTML] = microformat_interceptor因为我们的 action 需要一个单独的事件作为它的输入,我们还需要从它生成的数组中抽取第一个事件,随后我们将需要修改事件的 model,使之能在 contructor 方法中接收 HCalendar,但考虑到我们 model 的字段与 microformat 的属性名是共享的,修改 model 就不是什么大问题了。

那么假定我们已经支持了标准的 POST 请求、XML 和 JSON,为什么我们还要在 Rails 应用中支持 Microformats 呢?站在前沿的角度看,这个问题没必要伤脑筋。如果我们以标准的格式来发布我们的信息,这个格式对人、Web 浏览器和其它解析器有意义。我们肯定能够在搜索引擎中获得更高的评价,使我们的数据被纳入搜索引擎的索引中,从而让这些信息在互联网上广泛传播。

而它对数据接收也非常有意义,因为它发掘了我们的诉求。对你的 HTML 专家而言,学习 Microformats 不过是时间问题。相比之下,对付 JSON 或一个新的 XML schema 则是更复杂的任务。Microformats 在某个系统中生成的页面能够被作为你的 Web 服务的输入。一旦存在 Web 页面或系统间沟通的需要,数据就会被创建,而随着越来越多的发布工具开始支持 Microformats,使用你服务的用户将越来越多。

查看英文原文: Versatile RESTful APIs Beyond XML - - - - - -

作者简介:James Stewart 是一个醉心于 Rails 的 Web 开发者。他现在生活在美国,不过他正在迁往英国的过程中。他的博客主要关注 web 开发领域,地址为 http://jystewart.net/process/ 译者简介:魏泉,具有多年企业级开发经验,曾担任过博文视点出版公司的技术编辑,是《Spring 技术手册》《Spring 专业开发指南》的责任编辑。武汉大学 Google Camp 的创建者之一,关注 Web 发展的最新趋势。

2007 年 4 月 26 日 10:012314

评论

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

憋了3月,每天学习这些源码笔记,已拿蚂蚁金服Java岗P6 Offer

Java成神之路

Java 程序员 架构 面试 编程语言

谷歌大神太肝了,熬夜整理的源码笔记竟然有手写Spring,跪拜了

Java成神之路

Java 程序员 架构 面试 编程语言

LeetCode题解:188. 买卖股票的最佳时机 IV,动态规划,JavaScript,详细注释

Lee Chen

算法 LeetCode 前端进阶训练营

谷歌android!通宵都要看完这个Android关键技术点,威力加强版

欢喜学安卓

android 程序员 面试 移动开发

【LeetCode】二维区域和检索 - 矩阵不可变Java题解

HQ数字卡

算法 LeetCode 28天写作

severless架构演进思考杂谈

秋呈

Serverless 架构

哪些应用场景在使用语音连麦

anyRTC开发者

音视频 WebRTC RTC 语音社交

PingCode新成员Goals开放内测!

PingCode

项目管理 敏捷 敏捷开发 研发管理 研发效能

微服务框架相关技术整理

云流

架构 微服务

Java程序员福音!蚂蚁+字节+腾讯+龙湖地产+美团+滴滴+猿辅导1000+面试题总结

Java架构追梦

Java 架构 面试 金三银四 1000道面试题

直流电源防反接电路设计

不脱发的程序猿

嵌入式 28天写作 硬件设计 直流电源 防反接电路设计

【黑科技】爬虫也可以一键获取 [加载更多] 数据,无编码学爬虫之三。

梦想橡皮擦

Python 28天写作 3月日更

阿里开源SpringBoot全栈小册!Github已标星百万

程序员小毕

Java spring 架构 微服务 springboot

在敏捷项目管理情境下,如何做多项目管理?

PingCode

敏捷 敏捷开发 研发管理 研发效能 研发工具

开源分享 | 火遍全网的「蚂蚁呀嘿」教程来了!

百度开发者中心

paddle 开源项目 #飞桨#

2021年最新蚂蚁金服 Java 高级岗2000+面试题(含答案)通关秘籍

Java成神之路

Java 程序员 架构 面试 编程语言

连续两次入围Gartner魔力象限的Quick BI到底有何魔力?

Geek_459987

腾讯音乐-全民K歌iOS面经

iOSer

ios 面试题 腾讯大厂 金三银四跳槽

如果软件测试行业没门槛了,你开心么?

程序员一凡

软件测试 自动化测试 测试开发 测试工程师 黑盒测试

高效搜索信息,你需要掌握这些谷歌搜索技巧

LeanCloud

开发者 前端 工作效率

最新热点:钉钉公布低代码开发者画像,这些数据意味着什么?

优秀

重磅丨国资委下发通知,加快推进国有企业数字化转型

PingCode

团队管理 项目管理 研发管理 研发效能 研发工具

互联网公司的「敏捷开发」流程是怎么样的?典型的敏捷团队是什么样?

PingCode

敏捷 敏捷开发 研发管理 研发效能 研发工具

总结:近几年有哪些不错的scrum工具

PingCode

Scrum 敏捷 敏捷开发 研发管理 研发工具

Pgbouncer最佳实践:系列二

PostgreSQLChina

数据库 postgresql 开源 软件 开源社区

作为产品经理,如何分析和管理你的产品需求?

PingCode

敏捷开发 研发管理 需求管理 需求 研发工具

最新整理:Google/网易/腾讯/百度/华为面经(25个专题 1W字答案解析)

比伯

Java 编程 程序员 架构 面试

谷歌开发安卓系统!Android面试你必须要知道的那些知识,全网疯传

欢喜学安卓

android 程序员 面试 移动开发

与 Hadoop 对比,我是如何看待 Spark 技术?

会飞的鱼

hadoop spark 大数据处理 存储 大数据架构

力扣(LeetCode)刷题,简单+中等题(第30期)

不脱发的程序猿

程序人生 算法 LeetCode 面试刷题 28天写作

话说 synchronized

木子的昼夜

Java

2021 ThoughtWorks 技术雷达峰会

2021 ThoughtWorks 技术雷达峰会

超越XML格式的多样化REST风格API-InfoQ