写点什么

在 RESTful 应用程序中的超媒体

  • 2008-03-20
  • 本文字数:4656 字

    阅读完需:约 15 分钟

超媒体到底是什么?!

如果你有幸听说过 REST 架构风格,那么你也可能听说某些人认为统一接口是最重要的约束,尤其是该接口在制约资源上能被调用的方法方面。但是,你没有意识到的是,对于统一接口还有很多东西。特别是一个被笨拙地冠以“超媒体即应用状态引擎(hypermedia as the engine of application state)”名字的子约束,它可以认为是 REST 最重要的约束,在某种意义上它独力塑造了我们所熟知的 RESTful 系统的大部分的“形状”。

本文中我们将深入讨论这个约束,试图去搞清楚它的含义和理解它的价值。

定义

不幸的是, REST 论文没有在这个约束上展开,除了它的名字和对它实际应用的描述:

所以,模型应用是一个从一个状态迁移到下一个状态的引擎,迁移是通过对当前表述性集合中可选状态进行检查和选择而完成的。

尽管它给我们提供了一个有用的描述,但是在我看来,它不能帮助我们真正理解约束本身的作用域;约束真正允许的是什么,不允许的又是什么。由此开始,接下来还值得注意的是我们能从约束自身名字中获取些什么信息。

“应用状态”指的是一个状态,它决定了用户在完成一个任务的流程中所处的“位置”。例如,在做个人银行业务的时候,用户是正在浏览帐户余额,还是在填写账目付款单,或者是在订购新的支票?它们每个都是不同的应用状态。有些人错误地认为“状态”是指资源状态,在上面例子里,资源是指帐户余额或者近期付款清单。但“应用状态”和“资源状态”是不同的。

应用状态也被称为“会话状态”,该状态也是 REST 的“无状态”约束所指的状态,这种约束要求客户端独自维持状态。反之,如果你使用诸如 VNC 或者 Windows 远程桌面的远程会话技术,那么应用状态完全保存在服务器上。

Ted Nelson 在 1962 年创造了“超媒体”一词,是他发明的“超文本”泛化。鉴于超文本产生了内部互联的文本文档,那么超媒体将范围扩展到了任何形式的媒体。当然,两者的关键点都是我们可以在使用的内容嵌入链接。

约束实战

REST 在 2003/2004 年开始获得一些从事面向互联网服务开发者的关注——至少这些开发者确实将他们的服务冠以“REST”的绰号——最明显的是两个高调、自我标榜的“REST API”: Flickr Amazon 。有趣的是两个服务也同时提供了基于 SOAP 的接口,但是与“REST API”相比,这两个 SOAP API都没有显出更多的使用。最终,REST 社区拥抱了这些服务,并且将它们作为一种进一步解释在Web 上使用REST 风格的价值和魅力的工具。不幸的是,这些API 存在一个问题:它们不完全是RESTful 的,因为它们无视(至少)一个REST 约束。事实上,Flickr API(以及Amazon、del.icio.us 等等)的问题比我们在此讨论的要多得多,此处我们只关注那些和超媒体有关系的问题。

幸运的是,我们不需要为寻找这些问题花太多时间。以“flickr.contacts.getList”操作返回的样本数据为例,用户可使用该操作获得他们自己的联系人清单:

<contacts page="1" pages="1" perpage="1000" total="3"><br></br> <contact nsid="12037949629@N01" username="Eric" iconserver="1"<br></br> realname="Eric Costello"<br></br> friend="1" family="0" ignored="1" /><br></br> <contact nsid="12037949631@N01" username="neb" iconserver="1"<br></br> realname="Ben Cerveny"<br></br> friend="0" family="0" ignored="0" /><br></br> <contact nsid="41578656547@N01" username="cal_abc" iconserver="1"<br></br> realname="Cal Henderson"<br></br> friend="1" family="1" ignored="0" /><br></br></contacts>这里,“nsid”属性包含了一个表示单个联系人的唯一标识符,在这个例子里是其中的三个联系人。但是一旦客户端已经检索到了这个文档,接下来会怎样?如果他们想了解更多关于 Cal Henderson 的信息怎么办?通过快速检查 Flickr API 文档,可以发现有一个叫做"flickr.people.getInfo"的操作,它接收nsid 作为一个参数,返回那个nsid 字符串所标识联系人的更多信息。那么为了获得关于Cal 的更多信息,我们在HTTP GET 消息中需要使用的URI 将是:

http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=41578656547@N01这不是超媒体。一个超媒体解决方案会使用标准化的标识符——对于 Web 来说就是 URI——而非私有的标识符,这避免了客户端在从联系人清单文档浏览到个人信息文档过程中需要特定于 Flickr 的知识。如果采用标准标识符,那么第一个文档应该是:

<contacts page="1" pages="1" perpage="1000" total="3"><br></br> <contact nsid="http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=12037949629@N01" username="Eric" iconserver="1"<br></br> realname="Eric Costello"<br></br> friend="1" family="0" ignored="1" /><br></br> <contact nsid="http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=12037949631@N01" username="neb" iconserver="1"<br></br> realname="Ben Cerveny"<br></br> friend="0" family="0" ignored="0" /><br></br> <contact nsid="http://api.flickr.com/services/rest/?method=flickr.people.getInfo?auth_key=xxxx&user_id=41578656547@N01" username="cal_abc" iconserver="1"<br></br> realname="Cal Henderson"<br></br> friend="1" family="1" ignored="0" /><br></br></contacts>万事大吉,但是把这些变到超媒体对他们和他们的用户会有什么好处呢?

为了从一个应用状态前进到另一个应用状态,以 Flickr 现在的方式,要求客户端处理特定于 Flickr 的知识,这是另一种形式的私有应用。它不仅是私有的,而且即使在 Flickr API 本身内部它也不是一个一致的模型,因为从联系人清单文档浏览到联系人个人信息文档所需要的知识(如上面所述),不同于从联系人个人信息文档浏览到联系人个人照片清单所需要的知识(它是“flickr.photos.getContactsPublicPhotos”)。这给Flickr 提出了可发展性问题,因为即使对API 进行简单地扩展也轻易地要求传播新知识,需要依次修改客户端代码。一个诸如搜索引擎这样的普通客户端,也不能通过这些API 来给Flickr 内容建立索引,因为我确信搜索引擎的维护人员——或者其他使用这个应用程序模型的人——对于每次Ficlkr 扩展API 都需升级他们的软件不会有太多的兴趣。再次重申,这不仅仅限于超媒体:任何标准化的应用模型都会提供同样的好处。当然,超媒体模型已经证明它自己非常流行,即使那些使用它的人们没有意识到自己所做的事。

因此通过使用一个公共应用模型,它不仅仅是标准化的,而且总是稳定的,你可以通过准许消费者和生产者独立进化来降低两者间耦合。通过这种方法,新旧服务可以被组合在一起形成一个组合应用,新老客户端也可以合并成一个。我认为,一旦Web 可以让人们简单地在一个文档中包含一个指向几年前创作的页面和该内容消费者,当我们使用它时,完全可以无缝地浏览内容而无须下载一个新版本的浏览器。这都是特意设计的,决非偶然。

值得注意的是,Web 决没有垄断超媒体的使用。Email 是另一个已普及的应用,我们大家在互联网上每天都用到它,也是如此。每条Email 消息包含携带发信人和收信人的Email 地址的头,而拥有这些地址中的一个或者多个就足够发送另一条Email 消息了。

当我们在讨论这些鲜为人知的事实时,你可能也会有兴趣知道:Web 本身一个重要方面使用超媒体: robots.txt ,亦称漫游器排除文档(robot exclusion)。它的工作方式:如果网站希望搜索引擎不要给它们的某些内容建立索引,那么就在他们的“/robots.txt” URI 中简单地放置一个文件,该文件描述了哪些内容不希望被建立索引。但是,就我所知的是“几乎没有人链接到robots.txt 文件”。为什么会这样?“/robots.txt”是一个固定且众所周知的位置,特别是对于搜索引擎来说:给出任何URI,它们都能从URI 构建出这个站点域相应的robots.txt URI。尽管这不是超媒体,因为链接不是动态地从另一个页面发现的,搜索引擎恰恰认为这是一个优点。这不能说它是一个的解决方案,因为超媒体方式需要两次网络往返(一次是发现链接robots.txt 的页面,另一次是抓取页面),这对于所有的当事方都是一种负担。所以这也是一个关于超媒体成本的好例子。但是要牢记这点:只有在极少情况下这(译注:即不采用超媒体)真的才是最佳方式。和robots.txt 原因相同,站点地图可能也算一个;但是其他的诸如“favicon.ico”和Apple 新的 iPhone WebClip 特性,将可能从使用超媒体上获益;例如,那些图标可以被一个图像搜索引擎搜索到,而不需要更新搜索引擎软件。

在考虑超媒体时,另一个值得关注的技术是 WADL ,Web 应用描述语言。尽管它自称“RESTful 描述语言”,但是对它有一个重要的警告。考虑从这个从 WADL 文件中摘录的例子:

<resources base="http://service.example.com/myservices/"><br></br> <resource path="search"><br></br> <method name="GET" id="search"><br></br> <request><br></br> <param name="query" type="xsd:string" style="query" required="true"/><br></br> </request><br></br> <response><br></br> <representation mediaType="application/xml" element="yn:ResultSet"/><br></br> <fault status="400" mediaType="application/xml" element="ya:Error"/><br></br> </response><br></br> </method><br></br> </resource><br></br></resources>这个文件将一个“search”资源声明为一个“myservices”集合的一部分。通过声明使用 HTTP GET 和使用“query”参数,它描述了客户端从它选择的输入字符串构建一个 URI 的方式。

表面上看,它似乎是一个完美 RESTful 的、基于超媒体的解决方案,非常类似 HTML 表单(或者 URI 模板)的使用方式。那么警告是什么?问题在于 WADL 被消费的时机。某些对基于 Web 的解决方案感兴趣的 Web 服务支持者按照他们使用 WSDL 的方式(作为设计时部件)使用 WADL。但是,这样使用 WADL,类似于在编译浏览器的时候,开发一个具有内建知识(比方说 Google 主页表单)的 Web 浏览器:如果在部署浏览器后,Google 以不向后兼容的方式改变了这个表单(即,不仅仅增加一个新的可选参数),那么浏览器将无法使用那些资源 / 服务。使用具有 WADL 的超媒体约束意味着客户端应该在运行时消费 WADL。所以小心地选择你的 WADL 工具,因为那些试图帮助你的一些工具,可能帮倒忙。

结论

希望超媒体约束的价值现在看起来要明显多了。但是,不仅仅如此,我真正希望的是:当你决定使用它时,你能更好地了解哪些实践是你应该避免的。

记住:超媒体只是统一接口约束中的一员,因此对于后者更通用石蕊测试是:如果你正在开发客户端代码,而客户端代码假定它不能适用于所有资源(或者服务端“API”需要客户端的this,译注:服务端需要客户端的信息),那么你就没有使用统一接口。

关于作者

Mark Baker 在 SOA 和 Web 服务社区大名鼎鼎,源于他对推广 REST(表述性状态转移)架构风格的不断努力,以及他批评大多数标准和规范不了解什么成就并持续成就了 Web 的成功。

查看英文原文 Hypermedia in RESTful applications - - - - - -

译者简介:王志雄,长期从事软件开发工作,项目集中在 EAM 和设备点检管理领域。2004 年转入 JAVA 领域,曾经在项目中使用过 Hibernate、Struts、Spring 等。关心软件技术和相关工具的动态,将其中成熟的技术和工具应用到实际的项目之中。关心开源软件的发展动态以及软件过程和敏捷开发的实践探索。

2008-03-20 04:484115

评论

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

系统安全不求人:开发者必学的漏洞防御秘籍

巧手打字通

后端 系统安全 安全漏洞 SQL注入 xss攻击

软件测试学习笔记丨Mock的价值与实战

测试人

软件测试 测试开发

【YashanDB知识库】如何dump数据文件,转换rowid, 查询对应内容

YashanDB

yashandb 崖山数据库 yashandb知识库

TiDB 在线打标签实现副本调度应用实践

TiDB 社区干货传送门

实践案例 7.x 实践

环保行业怎么定义?需要用到堡垒机吗?

行云管家

等保 堡垒机 环保

获取淘宝商品详情API返回值的技术实现方案

技术冰糖葫芦

API Gateway API Explorer API 接口 API 测试 pinduoduo API

TiDB 助力东南亚领先电商 Shopee 业务升级

TiDB 社区干货传送门

昆仑万维 X TiDB|从 MySQL Cluster 到 TiDB 的升级之路

TiDB 社区干货传送门

黄东旭:“向量数据库”还是“向量搜索插件 + SQL 数据库”?

TiDB 社区干货传送门

【YashanDB知识库】由于hist_head$中analyze time小于tab$中analyze time导致的sql语句执行慢

YashanDB

yashandb 崖山数据库 yashandb知识库

“AI+Security”系列第3期(六):打造最懂安全的智能体-无极AI安全智能体平台落地与实践

云起无垠

使用TiDB企业版Lightning导入ORC文件到TiDB

TiDB 社区干货传送门

迁移 7.x 实践

打造未来社交:区块链社交DAO的颠覆性开发之路

区块链软件开发推广运营

交易所开发 dapp开发 链游开发 NFT开发 代币开发

人人都能手写的chrome插件,帮我省了1000多块钱

京东科技开发者

防御性编程:让系统坚不可摧

京东科技开发者

TiSplit 切分csv文件

TiDB 社区干货传送门

迁移 实践案例 管理与运维

获取淘宝商品详情API返回值的技术实现方案

代码忍者

API 测试 pinduoduo API

快速实现AI搜索!Fivetran 支持 Milvus 作为数据迁移目标

Zilliz

Milvus Zilliz 向量数据库 rag AI搜索

云栖实录 | 开源大数据全面升级:Native 核心引擎、Serverless 化、湖仓架构引领云上大数据发展

阿里云大数据AI技术

大数据 阿里云 Serverless 云栖大会 EMR

数字货币交易所开发与智能合约交易系统

区块链软件开发推广运营

交易所开发 链游开发 NFT开发 公链开发 代币开发

国产化新标杆:TiDB 助力广发银行新一代总账系统投产上线

TiDB 社区干货传送门

DDD建模 vs 传统开发

Bruce Talk

DDD 领域驱动设计DDD

OpenHarmony外设生态春笋行动 在2024开放原子开源生态大会上开启

最新动态

海外直播软件 Bigo 的 TiDB 4.0 线上实践

TiDB 社区干货传送门

目前东莞等保测评机构有哪些?电话多少?

行云管家

等保测评 过等保 东莞

TiDB多业务合并新玩法

TiDB 社区干货传送门

8.x 实践

TiDB 性能测试的几个优化点

TiDB 社区干货传送门

性能调优

AI与大数据的结合:如何从海量数据中提取价值

天津汇柏科技有限公司

大数据‘’ AI 人工智能

Linux服务器磁盘空间占用情况分析与清理指南

EquatorCoco

Linux 运维

Flink CDC:新一代实时数据集成框架

Apache Flink

大数据 flink 数据集成 Flink CDC 实时处理

在RESTful应用程序中的超媒体_SOA_Mark Baker_InfoQ精选文章