REST API 真的是 RPC 吗? 罗伊·菲尔丁似乎也这么认为
我认为自己对 REST 的了解显然是错误的——而且我并不孤单。 这个问题的引入时间较长,但似乎有必要,因为信息有点分散。 如果您已经熟悉这个主题,那么实际的问题将在最后出现。
从 Roy Fielding 的 REST API 必须是超文本的第一段来看,很明显,他认为自己的作品被广泛误解:
我对将任何基于 HTTP 的接口称为 REST API 的人数感到沮丧。 今天的示例是 SocialSite REST API。 那就是RPC。 它尖叫着 RPC。 显示的耦合太多,应该给予 X 评级。
Fielding 接下来列出了 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
相反,代理应该被迫发现所有 Foo 的 URI,例如,通过针对 /foos 发出 GET 请求。 (这些 URI 可能会遵循上面的模式,但这不是重点。)响应使用的媒体类型能够传达如何访问每个项目以及可以用它做什么,从而产生了上面的第三点。 因此,API 文档应重点解释如何解释响应中包含的超文本。
此外,每次请求 Foo 资源的 URI 时,响应都包含代理发现如何继续操作所需的所有信息,例如,通过 URI 访问关联资源和父资源,或者在创建后采取操作/删除资源。
整个系统的关键是响应由包含在媒体类型中的超文本组成,该媒体类型本身向代理传达继续进行的选项。 这与人类浏览器的工作方式没有什么不同。
但这只是我在这个特定时刻的最佳猜测。
菲尔丁发布了后续,其中他回应了有关他的讨论过于抽象的批评,缺乏示例,且术语丰富:
其他人会尝试以更直接或更适用于当今某些实际问题的方式来解读我所写的内容。 我可能不会,因为我太忙于解决下一个主题,准备会议,编写另一个标准,去某个遥远的地方旅行,或者只是做一些小事情,让我觉得我已经赚到了薪水。< /p>
因此,对于具有实用心态的 REST 专家来说,有两个简单的问题:您如何解释 Fielding 所说的内容以及在记录/实现 REST API 时如何将其付诸实践?
编辑:这个问题是一个例子,说明如果您没有所谈论的内容的名称,那么学习某些东西是多么困难。 本例的名称是“超媒体作为应用程序状态引擎”(HATEOAS)。
A large amount of what I thought I knew about REST is apparently wrong - and I'm not alone. This question has a long lead-in, but it seems to be necessary because the information is a bit scattered. The actual question comes at the end if you're already familiar with this topic.
From the first paragraph of Roy Fielding's REST APIs must be hypertext-driven, it's pretty clear he believes his work is being widely misinterpreted:
I am getting frustrated by the number of people calling any HTTP-based interface a REST API. Today’s example is the SocialSite REST API. That is RPC. It screams RPC. There is so much coupling on display that it should be given an X rating.
Fielding goes on to list several attributes of a REST API. Some of them seem to go against both common practice and common advice on SO and other forums. For example:
A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). ...
A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). ...
A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types. ...
The idea of "hypertext" plays a central role - much more so than URI structure or what HTTP verbs mean. "Hypertext" is defined in one of the comments:
When I [Fielding] say hypertext, I mean the simultaneous presentation of information and controls such that the information becomes the affordance through which the user (or automaton) obtains choices and selects actions. Hypermedia is just an expansion on what text means to include temporal anchors within a media stream; most researchers have dropped the distinction.
Hypertext does not need to be HTML on a browser. Machines can follow links when they understand the data format and relationship types.
I'm guessing at this point, but the first two points above seem to suggest that API documentation for a Foo resource that looks like the following leads to tight coupling between client and server and has no place in a RESTful system.
GET /foos/{id} # read a Foo
POST /foos/{id} # create a Foo
PUT /foos/{id} # update a Foo
Instead, an agent should be forced to discover the URIs for all Foos by, for example, issuing a GET request against /foos. (Those URIs may turn out to follow the pattern above, but that's beside the point.) The response uses a media type that is capable of conveying how to access each item and what can be done with it, giving rise to the third point above. For this reason, API documentation should focus on explaining how to interpret the hypertext contained in the response.
Furthermore, every time a URI to a Foo resource is requested, the response contains all of the information needed for an agent to discover how to proceed by, for example, accessing associated and parent resources through their URIs, or by taking action after the creation/deletion of a resource.
The key to the entire system is that the response consists of hypertext contained in a media type that itself conveys to the agent options for proceeding. It's not unlike the way a browser works for humans.
But this is just my best guess at this particular moment.
Fielding posted a follow-up in which he responded to criticism that his discussion was too abstract, lacking in examples, and jargon-rich:
Others will try to decipher what I have written in ways that are more direct or applicable to some practical concern of today. I probably won’t, because I am too busy grappling with the next topic, preparing for a conference, writing another standard, traveling to some distant place, or just doing the little things that let me feel I have I earned my paycheck.
So, two simple questions for the REST experts out there with a practical mindset: how do you interpret what Fielding is saying and how do you put it into practice when documenting/implementing REST APIs?
Edit: this question is an example of how hard it can be to learn something if you don't have a name for what you're talking about. The name in this case is "Hypermedia as the Engine of Application State" (HATEOAS).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
我认为你的解释大部分涵盖了它。 URI 是不透明的标识符,在大多数情况下,不应在用户代理用于访问应用程序的书签 URI 之外进行通信。
至于记录,这个问题已经做过好几次了。 您可以记录您的媒体类型及其包含的超链接控件(链接和表单)以及交互模型(如果您愿意)(请参阅 AtomPub)。
如果您记录 URI 或如何构建它们,那么您就做错了。
I think your explanation mostly covers it. URIs are opaque identifiers that should, for the most part, not be communicated beyond the bookmark URI that is used by the user agent to access the app.
As for documenting, this question has been done quite a few times. You document your media type, together with the hyperlink controls that it contains (links and forms), and the interaction model if you so wish (see AtomPub).
If you document the URIs or how to build them, you're doing it wrong.
你的解释对我来说似乎是正确的。 我确实相信菲尔丁的约束可以实际应用。
我真的很希望看到有人发布一些关于如何记录 REST 接口的好示例。 有很多糟糕的例子,有一些有效的例子来指导用户将是非常有价值的。
Your interpretation seems correct to me. I do believe that Fielding's constraints can be practically applied.
I really would like to see someone publish some good examples of how to document a REST interface. There are so many poor examples, have some valid ones to point users to would be very valuable.
我一直在寻找一个遵循 HATEOAS 编写的 API 的好示例,但很难找到一个(我发现 SunCloud API 和 AtomPub 的东西都很难应用于“正常”API 情况)。 因此,我尝试在我的博客上制作一个实际示例,遵循 Roy Fieldings 关于正确 REST 实现的含义的建议。 我发现很难想出这个例子,尽管它在原理上相当简单(只是在使用 API 而不是网页时令人困惑)。 我明白 Roy 的异议并同意,这只是正确实施 API 的思维方式的转变。
看一下:使用 Rest 的 API 示例
I have been looking for a good example of an API written following the HATEOAS and had trouble finding one (I found both the SunCloud API and AtomPub stuff difficult to apply to a "normal" API situation). So I tried making a realistic example on my blog that followed Roy Fieldings advice on what it means to be a proper REST implementation. I found it very difficult to come up with the example, despite the fact that it is fairly simple in principle (just confusing when working with an API as opposed to a webpage). I get what Roy was taking issue with and agree, it is just a shift in mindset to implement properly for an API.
Have a look: API Example using Rest
给出有关如何构建 URI 的说明的一个例外是,允许在超文本响应中发送 URI 模板,其中字段将由客户端使用超文本中的其他字段自动替换。 不过,这通常不会节省太多带宽,因为 gzip 压缩可以很好地处理 URI 的重复部分,无需为此烦恼。
关于 REST 和相关 HATEOAS 的一些很好的讨论:
(还有) 在 RESTFul API 中使用 HATEOAS
如何喝杯咖啡
The one exception to giving instruction on how to build URIs is that it is permissible to send a URI template in the hypertext response, with fields to be substituted automatically by the client, using other fields in the hypertext. This doesn't usually end up saving much bandwidth though since gzip compression will handle the repeated parts of URIs well enough to not bother with this.
Some good discussions on REST and the related HATEOAS:
Advantages Of (Also) Using HATEOAS In RESTFul APIs
How to GET a cup of coffee
对于那些感兴趣的人,我在 Sun Cloud API 中找到了 HATEOAS 实践的详细示例。
For those interested, I found a detailed example of HATEOAS in practice in the Sun Cloud API.
大多数人犯的错误是(至少我认为)在 REST 世界中,您不会记录您的“Rest 接口”,您记录的是一种媒体类型,独立于您的服务器或服务。
The thing that most people get wrong is that (at least i think) in the REST world you don't document your "Rest interface", what you document is a media type, independently of your server or service.
我认为 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。
建议进一步阅读的链接:
I think over the number of years that REST has been out there now, technologists have come to terms with the concept of a Resource and what really is or isn't RESTful.
According to the Richardson Maturity Model, there are 4 levels (0-3) that define how RESTful your API is, with 3 meaning a truly RESTful API, just as Roy Fielding intended it to be.
Level 0 is when you have one entry point URI - like SOAP.
Level 1 means the API is able to distinguish between different resources, and has more than one entry points - still smells of SOAP.
Level 2 is when you use HTTP verbs - GET, POST, DELETE primarily. This is the level at which REST really comes into picture.
At Level 3, you start using hypermedia controls to make your API truly RESTful.
Suggested links for further reading:
完全正确。 另外我还要指出的是,只要模式来自从服务器接收的文档(OpenSearch 就是一个合适的示例),URI 模板在 RESTful 应用程序中就完全没问题。 对于 URI 模板,您记录它们的使用位置以及模板中预期的占位符是什么,但不记录模板本身。 与 Wahnfrieden 所说的略有相反,这并不是一个例外。
例如,在我的工作中,我们有一个内部域管理系统,并且服务文档指定了两个 URI 模板:一个用于为域资源生成最佳猜测 URI,另一个用于构建用于查询域可用性的 URI。 仍然可以通过域集合进行分页来找出给定域的 URI,但是考虑到它管理的域数量巨大,这对于客户端来说是不可行的,因此给他们一种方式来猜测给定域的 URI。从客户端的角度来看,域资源的 URI 可能在实现的简便性和服务器的带宽方面是一个巨大的胜利。
关于你的问题:我们的规范文档是公开的资源、各种方法对这些资源的影响、使用的表示媒体类型及其模式,以及这些表示中的 URI 指向什么类型的资源。
我们还包括非规范(信息性)文档,其中附有免责声明,要求不要过多阅读文档中提到的 URI,其中提供了典型客户端-服务器交互的示例。 这使得相当抽象的规范性文档变得具体化。
Absolutely correct. I'd note in addition that that URI templates are perfectly fine within a RESTful application so long as the patterns are from documents received from the server (OpenSearch being a suitable example). For URI templates, you document where they're being used and what the expected placeholders in the template are, but not the templates themselves. Slightly contrary to what Wahnfrieden said, this isn't an exception.
For example, at my work we have a internal domain management system, and the service document specifies two URI templates: one for producing a best guess URI for a domain resource, and another for constructing a URI for querying domain availability. It's still possible to page through the domains collection to figure out what the URI of a given domain is, but given the immense number of domains it manages, this wouldn't be feasible for the client, so giving them a way to guess what the URI of a domain resource might be is a huge win in terms of ease of implementation from the client's perspective, and bandwidth from the server's.
On to your question: Our normative documentation is exposed resources, the effect of various methods on those resources, and the representation media types used and their schemas, and what kind of resources the URIs in those representions point to.
We also include non-normative (informative) documentation that has attached to it a disclaimer not to read too much into the URIs mentioned in the document, which gives examples of typical client-server interactions. This puts the rather abstract normative documentation in concrete terms.
假设调用
GET /foos/createForm
来获取表单字段值,在创建POST /foos
时必须提供该值。 现在,根据 Fielding 的提议,这个特定的 URL,即用于创建 foos 的 1 应该在GET /foos/createForm
的响应中作为提交操作链接提及,对吗?那么将动作映射到众所周知的 Http 动词到动作有什么好处,“约定优于代码/配置”的事情就失效了。
Let's assume
GET /foos/createForm
is invoked to get form fields values for which must be provided when we go to createPOST /foos
. Now this particular URL i.e the 1 used to create foos should be mentioned within the response forGET /foos/createForm
as a submit action link according to Fielding's proposition, right ?Then what is the benefit of mapping actions to well-known Http verbs to actions, "convention over code/config" thing is nullified.