【问题标题】:Is That REST API Really RPC? Roy Fielding Seems to Think So那个 REST API 真的是 RPC 吗?罗伊菲尔丁似乎这么认为
【发布时间】:2009-07-22 09:41:21
【问题描述】:

我认为我对 REST 了解的大部分内容显然是错误的——而且我并不孤单。这个问题的引文很长,但似乎很有必要,因为信息有点分散。如果您已经熟悉此主题,则实际问题在最后。

从 Roy Fielding 的REST APIs must be hypertext-driven 的第一段,很明显他认为他的工作被广泛误解:

我对将任何基于 HTTP 的接口称为 REST API 的人数感到沮丧。今天的例子是SocialSite REST API。那就是RPC。它尖叫 RPC。展示的耦合太多了,应该给它一个 X 评级。

Fielding 继续列出 REST API 的几个属性。其中一些似乎违背了 SO 和其他论坛上的常见做法和常见建议。例如:

  • 除了初始 URI(书签)和一组适合目标受众的标准化媒体类型(即任何可能使用API)。 ...

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

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

“超文本”的概念起着核心作用——比 URI 结构或 HTTP 动词的含义更重要。 “超文本”在其中一个 cmets 中定义:

当我 [Fielding] 说超文本时,我的意思是信息和控制的同时呈现,使得信息成为用户(或自动机)获得选择和选择动作的可供性。超媒体只是对文本意味着在媒体流中包含时间锚点的扩展;大多数研究人员已经放弃了这种区别。

超文本在浏览器上不需要是 HTML。机器在了解数据格式和关系类型后就可以跟踪链接。

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

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

相反,应该强制代理发现所有 Foos 的 URI,例如,通过对 /foos 发出 GET 请求。 (这些 URI 可能会遵循上面的模式,但这不是重点。)响应使用的媒体类型能够传达如何访问每个项目以及可以用它做什么,从而产生了上面的第三点.因此,API 文档应着重说明如何解释响应中包含的超文本。

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

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

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

菲尔丁在follow-up 上发帖回应批评称他的讨论过于抽象、缺乏示例且行话丰富:

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

那么,对于 REST 专家来说,有两个简单的问题,有实用的思维方式:您如何解释 Fielding 所说的内容,以及在记录/实施 REST API 时如何将其付诸实践?

编辑:这个问题是一个例子,说明如果你没有为你所谈论的内容命名,那么学习一些东西是多么困难。本例中的名称是“作为应用程序状态引擎的超媒体”(HATEOAS)。

【问题讨论】:

  • 约翰,里奇只是在解释他的心态发生了变化。没有什么主观或争论的。投票保持开放 - 这是我在 SO 上看到的标记为“休息”的更好问题之一。
  • Keith,“解释思维方式的变化”是他应该在博客上做的事情,而不是 SO。
  • 他不是在解释他的心态变化,而是在问他的理解是否准确。
  • 优秀的总结。我从这个问题中学到的东西比从大多数答案中学​​到的更多。

标签: rest hateoas


【解决方案1】:

我认为您的解释主要涵盖了它。 URI 是不透明的标识符,在大多数情况下,不应在用户代理用于访问应用程序的书签 URI 之外进行通信。

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

如果您记录 URI 或如何构建它们,那么您做错了。

【讨论】:

  • 这仍然是真的吗?像 Ionspec 这样的 API 响应规范有意将这些 URI 作为响应的一部分。
  • 是的,他们有。在这一点上,问题是要弄清楚那些记录在案的 URI 是否只是应用程序的入口点,它们保证会保留下来(其中一些并不少见且非常有用),或者是否因为人们想要代码生成,这些 URI 是从直接在代码中的规范,防止服务器让客户端知道它是如何做事的。如果客户认为由于该合同而知道,那么您不是在超媒体中,而是在现代 openapi 肥皂模型中,与 18 年前遇到的问题相同。
  • 真实情况是,在过去 11 年中,许多 API 文档语言已经兴起,但基本面并没有改变。我相信发现这些链接的价值,或者至少是 URI 模板发现,在于构建可以动态使用这些链接的可重用通用客户端代码,从而允许服务器端的许多实现重用相同的客户端代码。 URI 嵌入继续使此类场景变得更加困难,但如果您使用这些格式,则倾向于将生成的客户端与这些规范紧密耦合,因此您已经失去了该功能。
  • "如果客户认为由于该合同而知道,那么您不在超媒体中,您进入的是现代 openapi 肥皂模型,您会遇到与 18 相同的问题几年前。”你能解释一下你的意思吗?
【解决方案2】:

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

我真的很想看到有人发布一些关于如何记录 REST 接口的好例子。糟糕的例子太多了,有一些有效的例子可以向用户指出将非常有价值。

【讨论】:

  • 哇。那个资源模型页面让我热泪盈眶。让我们希望这会引发一种趋势。
  • 很遗憾,这基本上是网络上此类 API 的唯一示例!更糟糕的是,根本没有遵循原则的客户端代码的好例子(我发现)。
  • @DarrelMiller 但是这些媒体类型不是太“具体”了吗?在我看来,他们的 API 确实只使用了一个 MIME:application/json,并且资源模型确实是关系。我是否误解了 REST 的这一方面?我还阅读了one of your SO answers,它似乎指出应该避免那些“单一属性”合同......
  • @RichApodaca 您的链接已死于痢疾。 web.archive.org/web/20170409132237/https://kenai.com/projects/…
【解决方案3】:

我一直在寻找遵循 HATEOAS 编写的 API 的一个很好的示例,但找不到一个(我发现 SunCloud API 和 AtomPub 的东西都很难应用于“正常”的 API 情况)。因此,我尝试在我的博客上制作一个真实的示例,该示例遵循 Roy Fieldings 关于正确实现 REST 意味着什么的建议。我发现很难想出这个例子,尽管它在原则上相当简单(只是在使用 API 而不是网页时令人困惑)。我明白 Roy 的问题并同意,这只是为了正确实施 API 的思维方式的转变。

看看:API Example using Rest

【讨论】:

    【解决方案4】:

    提供有关如何构建 URI 的说明的一个例外是,允许在超文本响应中发送 URI 模板,其中的字段由客户端自动替换,使用超文本中的其他字段。这通常不会最终节省太多带宽,因为 gzip 压缩会很好地处理 URI 的重复部分,而不必为此烦恼。

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

    Advantages Of (Also) Using HATEOAS In RESTFul APIs

    How to GET a cup of coffee

    【讨论】:

      【解决方案5】:

      对于那些感兴趣的人,我在Sun Cloud API 中找到了 HATEOAS 实践的详细示例。

      【讨论】:

      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        我认为,在 REST 出现的这些年里,技术人员已经开始接受资源的概念,以及什么是真正的 RESTful 或不是 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。

        进一步阅读的建议链接:

        【讨论】:

          【解决方案8】:

          完全正确。另外我要注意的是,只要模式来自从服务器接收到的文档(OpenSearch 是一个合适的例子),URI 模板在 RESTful 应用程序中非常好。对于 URI 模板,您需要记录它们的使用位置以及模板中预期的占位符是什么,而不是模板本身。与 Wahnfrieden 所说的稍有相反,这不是个例。

          例如,在我的工作中,我们有一个内部域管理系统,并且服务文档指定了两个 URI 模板:一个用于为域资源生成最佳猜测 URI,另一个用于构造一个用于查询域可用性的 URI。仍然可以翻阅域集合以找出给定域的 URI 是什么,但鉴于它管理的域数量巨大,这对客户端来说是不可行的,所以给他们一种方法来猜测什么是从客户端的角度来看,域资源的 URI 在易于实施和从服务器的带宽方面可能是一个巨大的胜利。

          关于您的问题:我们的规范性文档是公开的资源、各种方法对这些资源的影响、使用的表示媒体类型及其架构,以及这些表示中的 URI 指向的资源类型。

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

          【讨论】:

          • 带外提供 URI 模板作为 API 的一部分很好。请不要将此称为 REST,因为它不是。这是大量的耦合,而 REST 正是为了避免这种情况。但就像你说的,REST 并不适用于所有应用程序。所以不要假装每个应用程序都是 REST。
          • 其实我同意。我相信这就是我所说的。但是,我真的看不出有什么好的理由来提供带外的 URI 模板。
          【解决方案9】:

          假设调用GET /foos/createForm 来获取表单字段值,当我们创建POST /foos 时必须为其提供这些值。现在这个特定的 URL,即用于创建 foos 的 1 应该在 GET /foos/createForm 的响应中被提及,作为根据菲尔丁的提议的提交操作链接,对吧?
          那么将动作映射到众所周知的 Http 动词到动作有什么好处,“代码/配置的约定”这件事被取消了。

          【讨论】:

            猜你喜欢
            • 2020-10-15
            • 1970-01-01
            • 1970-01-01
            • 2011-08-10
            • 1970-01-01
            • 1970-01-01
            • 2019-01-30
            • 2014-02-02
            • 1970-01-01
            相关资源
            最近更新 更多