REST API真的是RPC吗?罗伊·菲尔丁(Roy Fielding)似乎这么认为


99

我以为我对REST了解的很多知识显然是错误的-而且我并不孤单。这个问题的引入时间很长,但是由于信息有点分散,因此似乎很有必要。如果您已经熟悉此主题,那么实际问题就在最后。

从Roy Fielding的REST API的第一段开始,它必须是超文本驱动的,很明显,他认为自己的工作被广泛误解了:

人们对将任何基于HTTP的接口称为REST API的人数感到沮丧。今天的示例是SocialSite REST API。那就是RPC。它尖叫RPC。显示器上的耦合太多,因此应给定X等级。

字段继续列出了REST API的几个属性。他们中的一些人似乎与SO和其他论坛上的常规做法和常规建议背道而驰。例如:

  • 输入REST API时,除了初始URI(书签)和适合目标受众(即,可能会使用该API的任何客户端都希望理解)的一组标准化媒体类型外,没有其他先验知识。...

  • REST API不得定义固定的资源名称或层次结构(客户端和服务器的明显结合)。...

  • REST API应该花费几乎所有的描述性精力来定义用于表示资源和驱动应用程序状态的媒体类型,或者为现有标准媒体类型定义扩展关系名称和/或启用超文本的标记。...

“超文本”的概念起着核心作用-比URI结构或HTTP动词的含义要重要得多。在以下注释之一中定义了“超文本”:

当我[Fielding]说超文本时,我的意思是信息和控件的同时呈现,以使该信息成为用户(或自动机)通过其获得选择并选择动作的能力。超媒体只是文本在媒体流中包含时间锚点的扩展。大多数研究人员都放弃了这一区别。

在浏览器中,超文本不需要是HTML。当机器了解数据格式和关系类型时,它们可以跟随链接。

我正在猜测,但是上面的前两点似乎表明Foo资源的API文档(如下所示)导致客户端和服务器之间的紧密耦合,并且在RESTful系统中没有位置。

GET   /foos/{id}  # read a Foo
POST  /foos/{id}  # create a Foo
PUT   /foos/{id}  # update a Foo

相反,应该通过(例如)对/ foos发出GET请求来强制代理发现所有Foos的URI。(这些URI可能遵循上面的模式,但这不重要。)响应使用一种媒体类型,该媒体类型能够传达如何访问每个项目以及如何执行每个项目,从而导致上面的第三点。因此,API文档应集中于解释如何解释响应中包含的超文本。

此外,每次请求Foo资源的URI时,响应都包含代理程序发现如何进行操作所需的所有信息,例如,通过其URI访问关联资源和父资源,或在创建后采取行动/删除资源。

整个系统的关键在于,响应由媒体类型中包含的超文本组成,该媒体类型本身会传达给代理选项以进行处理。这与浏览器为人类工作的方式没有什么不同。

但这只是我在这个特定时刻的最佳猜测。

菲尔丁发表了一篇后续文章,他在评论中回应了批评,即他的讨论过于抽象,缺乏实例且行话丰富:

其他人将尝试以更直接的方式或适用于当今某些实际问题的方式来解读我所写的内容。我可能不会,因为我太忙于处理下一个主题,准备会议,编写另一个标准,前往某个遥远的地方,或者只是做些让我觉得自己已经赚到了薪水的小事。

因此,针对REST专家的两个简单问题是,他们具有实用的思维方式:如何解释Fielding在说什么,以及在记录/实现REST API时如何将其付诸实践?

编辑:这个问题是一个例子,说明如果您不知道所谈论的内容,那么学习一些东西就会有多困难。在这种情况下,名称为“作为应用程序状态引擎的超媒体”(HATEOAS)。


26
约翰,里奇只是在解释自己的思维方式的变化。没有任何主观或争论的内容。投票保持开放-这是我在SO上看到的标记为“休息”的更好问题之一。
基思·高恩

4
Keith,“解释思维方式的改变”是他应该在自己的博客中进行的工作,而不是SO。
约翰·桑德斯

13
他没有在解释自己的思维方式变化,而是在问他的理解是否正确。
aehlke

4
优秀的总结。我从这个问题中学到的比从大多数答案中学​​到的更多。
Martin Konecny 2014年

Answers:


21

我认为您的解释大都涵盖了这一点。URI是不透明的标识符,在大多数情况下,URI不应超出用户代理用来访问应用程序的书签URI的范围之外。

至于记录,这个问题已经做了好几次了。您可以记录媒体类型,以及其中包含的超链接控件(链接和表单)以及交互模型(如果需要)(请参见AtomPub)。

如果您记录URI或如何构建URI,那就错了。


这仍然是真的吗?有一些API响应规范(例如Ionspec)已将这些URI故意作为响应的一部分。
肖恩·皮安卡

是的,他们有。到那时,要弄清楚这些记录的URI是否只是应用程序的入口点,可以保证存在(其中一些并不罕见且非常有用),还是因为人们希望生成代码,所以这些是从中嵌入的?规范直接包含在代码中,阻止服务器让客户端知道它如何执行操作。如果客户认为由于该合同而知道,那么您就不是超媒体用户,而是进入了现代openapi soap模型,而该模型与您18年前遇到的问题相同。
SerialSeb

真实的是,过去11年中出现了许多API文档语言,但是基本原理没有改变。我相信发现这些链接(至少是URI模板发现)的价值在于构建可动态使用它们的可重用通用客户端代码,从而允许服务器端的许多实现复用相同的客户端代码。URI嵌入继续使这种情况变得更加困难,但是,如果使用这些格式,则倾向于将生成的客户端与这些规范紧密结合在一起,因此您已经失去了该功能。
SerialSeb

8

您的解释对我来说似乎是正确的。我相信菲尔丁的约束可以实际应用。

我真的很想看到有人发布一些有关如何记录REST接口的很好的示例。有这么多不良的例子,有一些有效的例子来指出用户将是非常有价值的。


2
哇。该“资源模型”页面引起了我的注意。我们希望这会开始成为一种趋势。
Darrel Miller 2009年

遗憾的是,这基本上是网络上此类API 的唯一示例!更糟糕的是,根本没有很好的客户端代码示例遵循我所发现的原理。
jkp

1
@DarrelMiller但是这些媒体类型不是太“特定”了吗?在我看来,他们的API实际上仅使用一种MIME:,application/json而资源模型才是真正的关系。我是否误解了REST的这一方面?我还读 了您的一个SO答案,该答案似乎指出应避免使用那些“单一属性”合同...
edsioufi 2013年


5

我一直在寻找遵循HATEOAS编写的API的一个很好的示例,但是找不到一个示例(我发现SunCloud API和AtomPub都很难应用于“正常” API情况)。因此,我尝试在我的博客上提出一个现实的示例,该示例遵循Roy Fieldings的建议,即成为正确的REST实现意味着什么。我发现很难举这个例子,尽管它在原理上相当简单(在使用API​​而不是网页时会感到困惑)。我得到Roy的质疑并同意,这只是对API正确实现的观念转变。

看看:使用Rest的API示例


4

给出有关如何构建URI的指令的一个例外是,允许在超文本响应中发送URI模板,并使用超文本中的其他字段将字段由客户端自动替换。尽管gzip压缩将足够好地处理URI的重复部分,而不会对此造成困扰,但这通常不会节省太多带宽。

关于REST和相关HATEOAS的一些很好的讨论:

在RESTFul API中使用(也)使用HATEOAS的优点

如何喝杯咖啡



4

大多数人都会犯错的是(至少我认为),在REST世界中,您没有记录“ Rest接口”,而记录的是一种媒体类型,与服务器或服务无关。


2

我认为REST已经存在了很多年,技术人员已经接受了资源的概念以及RESTful的真正含义。

根据Richardson成熟度模型,有4个级别(0-3)定义了您的API的RESTful程度,其中3个含义是真正的RESTful API,就像Roy Fielding所希望的那样。

级别0是指只有一个入口点URI(例如SOAP)的情况。

级别1表示API能够区分不同的资源,并且具有多个入口点-仍然带有SOAP的味道。

级别2是您使用HTTP动词的时间-主要是GET,POST,DELETE。这是REST真正发挥作用的级别。

在第3级,您开始使用超媒体控件使您的API 真正成为 RESTful。

建议的链接,供进一步阅读:


1

完全正确。我还要注意,只要模式是来自服务器接收的文档的,URI模板在RESTful应用程序中就可以很好地使用(OpenSearch是一个合适的例子)。对于URI模板,您要记录它们的使用位置以及模板中预期的占位符,而不是模板本身。与沃恩弗里登所说的略有不同,这也不例外。

例如,在我的工作中,我们有一个内部域管理系统,服务文档指定了两个URI模板:一个用于为域资源生成最佳猜测URI,另一个用于构建用于查询域可用性的URI。仍然可以浏览domains集合以弄清楚给定域的URI是什么,但是鉴于其管理的大量域,这对于客户端来说是不可行的,因此为他们提供了一种猜测从客户端的角度来看,实现的简便性和服务器的带宽方面,域资源的URI可能是一个巨大的胜利。

关于您的问题:我们的规范性文档包含以下方面:资源,各种方法对这些资源的影响,所使用的表示形式媒体类型及其模式,以及这些表示形式中URI指向的资源类型。

我们还包括非规范性(信息性)文档,该文档附加了免责声明,即不要过多地阅读文档中提到的URI,这提供了典型的客户端-服务器交互的示例。这使相当抽象的规范性文档变得具体。


1
带外提供URI模板作为API的一部分很好。请不要将此称为REST,因为它不是。这是大量的耦合,而正是要避免使用REST。但是就像您说的那样,REST并非适用于每个应用程序。因此,不要假装每个应用程序都是REST。
aehlke

1
其实,我同意。我相信我就是这么说的。但是,我真的看不出有任何理由提供带外URI模板。
基思·高恩

0

假设GET /foos/createForm调用来获取表单字段值,当我们创建时必须为其提供值POST /foos。现在,GET /foos/createForm根据Fielding的主张,应该在响应中提及该特定URL(即用于创建foo的1)作为提交操作链接,对吗?
然后,将动作映射到著名的Http动词到动作的好处是什么,“通过代码/配置进行约定”无效。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.