提问人:mhdwrk 提问时间:12/3/2013 最后编辑:EJoshuaS - Stand with Ukrainemhdwrk 更新时间:10/13/2022 访问量:28710
REST HATEOAS(成熟度等级 3)有多大用处/重要性?[已结束]
How useful/important is REST HATEOAS (maturity level 3)? [closed]
问:
我正在参与一个项目,一些高级团队成员认为 REST API 必须符合 HATEOAS 标准并实现所有 Richardson 的成熟度级别 (http://martinfowler.com/articles/richardsonMaturityModel.html)!
AFAIK 大多数 REST 实现都不符合 HATEOAS 标准,应该有充分的理由让更多人不这样做。我能想到一些原因,比如增加复杂性、缺乏框架(服务器和客户端)以及性能问题。
你觉得怎么样?你有没有在现实世界的项目中使用过HATEOAS的经验?
答:
REST社区中没有人说REST很容易。HATEOAS 只是增加 REST 架构难度的方面之一。
人们不做 HATEOAS 是出于你建议的所有原因:这很困难。它增加了服务器端和客户端的复杂性(如果你真的想从中受益的话)。
然而,今天有数十亿人体验到REST的好处。您知道亚马逊的“结账”URL是什么吗?我没有。然而,我每天都可以结账。该 URL 是否已更改?我不知道,我不在乎。
你知道谁在乎吗?任何编写过屏幕抓取的亚马逊自动化客户端的人。可能煞费苦心地嗅探网络流量、阅读 HTML 页面等以找到何时调用哪些链接以及使用什么有效负载的人。
一旦亚马逊改变了他们的内部流程和URL结构,那些硬编码的客户端就失败了——因为链接坏了。
然而,休闲的网上冲浪者能够整天购物,几乎没有障碍。
这就是REST在起作用,它只是被人类增强了,人类能够解释和直观地理解基于文本的界面,识别带有购物车的小图形,并弄清楚它的实际含义。
大多数编写软件的人不会这样做。大多数编写自动化客户端的人并不在乎。大多数人发现,当他们的客户崩溃时修复他们,比一开始就设计应用程序不破坏更容易。大多数人根本没有足够的客户来解决问题。
如果您正在编写一个内部 API,以便在两个系统之间与流量两端的专家技术支持和 IT 进行通信,他们能够快速、可靠地传达更改,并制定更改计划,那么 REST 不会给您带来任何好处。你不需要它,你的应用程序不够大,而且它的寿命不够长,没有意义。
拥有大量用户群的大型网站确实存在此问题。他们不能只是要求人们在与系统交互时一时兴起更改他们的客户端代码。服务器的开发计划与客户端的开发计划不同。对 API 的突然更改对于所有相关人员来说都是不可接受的,因为它会扰乱双方的流量和运营。
因此,像这样的操作很可能会从 HATEOAS 中受益,因为它更容易进行版本控制,更容易让旧客户端迁移,更容易向后兼容。
将大部分工作流委托给服务器并根据结果执行操作的客户端比不这样做的客户端对服务器更改的可靠性要强得多。
但大多数人不需要这种灵活性。他们正在为 2 或 3 个部门编写服务器代码,这都是内部使用的。如果它坏了,他们会修复它,并且他们已经将其纳入他们的正常操作中。
灵活性,无论是来自REST还是其他任何东西,都会滋生复杂性。如果你想要它简单、快速,那么你就不要让它变得灵活,你“只是去做”,然后就完成了。当你向系统添加抽象和取消引用时,事情就会变得更加困难,更多的样板,更多的代码需要测试。
REST的大部分内容都未能解决“你不需要它”的要点。当然,直到你这样做。
如果你需要它,那就使用它,并按照它的布局使用它。REST不是在HTTP上来回推送东西。它从来没有,它比这高得多。
但是当你确实需要REST并且你确实使用REST时,那么HATEOAS是必要的。它是软件包的一部分,也是使其发挥作用的关键。
例:-为了更好地理解它,让我们看一下以下从服务器检索 id 为 123 的用户的响应 ():http://localhost:8080/user/123
{
"name": "John Doe",
"links": [{
"rel": "self",
"href": "http://localhost:8080/user/123"
},
{
"rel": "posts",
"href": "http://localhost:8080/user/123/post"
},
{
"rel": "address",
"href": "http://localhost:8080/user/123/address"
}
]
}
评论
我在一些实际项目中使用过 HATEOAS,但与 Richardson 的解释不同。如果这是你的老板想要的,那么我想你应该去做。我认为 HATEOAS 的意思是您的资源应该包括 HTML 文档类型、指向相关资源的超链接和 HTML 表单,以公开 GET 以外的动词的功能。(这是当 Accept 类型为 text/html 时 - 其他内容类型不需要这些附加功能。我不知道整个应用程序中的所有REST资源必须粘合在一起的信念从何而来。网络应用程序应包含多个资源,这些资源可能直接相关,也可能不直接相关。或者为什么认为 XML、JSON 和其他类型的需要遵循这一点。(HATEOAS 是特定于 HTML 的。
是的,我在 API 中有一些超媒体的经验。以下是一些好处:
可探索的 API: 这听起来可能微不足道,但不要低估可探索 API 的强大功能。浏览数据的能力使客户端开发人员更容易构建 API 及其数据结构的心智模型。
内联文档: 使用 URL 作为链接关系可以将客户端开发人员指向文档。
简单的客户端逻辑: 仅遵循 URL 而不是自己构造 URL 的客户端应该更容易实现和维护。
服务器拥有 URL 结构的所有权: 使用超媒体会删除客户端对服务器使用的 URL 结构的硬编码知识。
将内容卸载到其他服务: 将内容卸载到其他服务器(例如 CDN)时,超媒体是必要的。
使用链接进行版本控制: 超媒体有助于对 API 进行版本控制。
同一服务/API 的多个实现: 当存在同一服务/API 的多个实现时,超媒体是必不可少的。例如,服务可以是一个博客 API,其中包含用于添加帖子和评论的资源。如果服务是根据链接关系而不是硬编码 URL 来指定的,则同一服务可以在不同的 URL 上多次实例化,由不同的公司托管,但仍可通过单个客户端通过同一组定义良好的链接进行访问。
您可以在此处找到对这些要点的深入解释:http://soabits.blogspot.no/2013/12/selling-benefits-of-hypermedia.html
(这里有一个类似的问题:https://softwareengineering.stackexchange.com/questions/149124/what-is-the-benefit-of-hypermedia-hateoas 我给出了相同的解释)
评论
Richardson 的成熟度等级 3 很有价值,应该被采用。约恩·怀尔特(Jørn Wildt)已经总结了一些优点,威尔特(Wilt)的另一个答案很好地补充了这一点。
然而,Richardson 的成熟度 3 级与 Fielding 的 HATEOAS 不同。Richardson 的成熟度级别 3 仅与 API 设计有关。Fielding 的 HATEOAS 也是关于 API 设计的,但也规定客户端软件不应假设资源具有超出媒体类型定义的结构的特定结构。这需要一个非常通用的客户端,例如 Web 浏览器,它不了解特定网站。由于 Roy Fielding 创造了 REST 一词,并将 HATEOAS 设置为符合 REST 的要求,那么问题来了:我们是否要采用 HATEOAS,如果没有,我们是否仍然可以将我们的 API 称为 RESTful?我认为我们可以。让我解释一下。
假设我们已经实现了 HATEOAS。应用程序的客户端现在非常通用,但最有可能的是,用户体验很糟糕,因为如果不了解资源的语义,就无法定制资源的表示形式以反映这些语义。如果资源“car”和资源“house”具有相同的媒体类型(例如 application/json),则它们将以相同的方式呈现给用户,例如作为属性表(名称/值对)。
但是,好吧,我们的 API 确实是 RESTful。
现在,假设我们在此 API 之上构建第二个客户端应用程序。第二个客户端违反了 HATEOAS 的想法,并且具有有关资源的硬编码信息。它以不同的方式显示汽车和房屋。
API 还能称为 RESTful 吗?我认为如此。API 的一个客户端违反了 HATEOAS,这不是 API 的错。
我建议构建 RESTful API,即理论上可以实现通用客户端的 API,但在大多数情况下,您需要一些关于客户端中资源的硬编码信息才能满足可用性要求。不过,请尽量少地进行硬编码,以减少客户端和服务器之间的依赖关系。
我在我的REST实现模式中包含了一个名为JAREST的关于HATEOAS的部分。
我们正在构建一个 REST 级别 3 API,其中我们的响应是 HAL-Json。 HATEOAS 非常适合前端和后端,但它也带来了挑战。我们做了一些自定义/添加,用于管理 HAL-Json 响应中的 ACL(这不会违反 HAL-Json 标准)。
我看到的 HATEOAS 的最大优势是,我们不需要在前端应用程序上写入/猜测任何 URL。您只需要一个入口点(),从那里开始,您可以使用响应中提供的链接或模板化链接浏览资源。
这样可以很容易地处理版本控制,重命名/替换 url,在不破坏前端代码的情况下使用其他关系扩展资源。https://hostname
在前端缓存资源是使用自链接的小菜一碟。 我们还通过套接字连接将资源推送到客户端,因为这些资源也是在 HAL 中呈现的,因此我们可以轻松地以相同的方式将它们添加到缓存中。
使用 HAL-Json 的另一个优点是,响应模型应该是什么样子很清楚,因为应该遵循一个记录在案的标准。
我们的自定义之一是,我们在自链接对象中添加了一个操作对象,该对象向前端公开允许经过身份验证的用户对相应资源(、、、、)执行哪些操作或 CRUD 操作。像这样,我们的前端 ACL 完全由我们的 REST API 响应决定,将这一责任完全转移到后端模型上。create:POST
read:GET
update:PUT
edit:PATCH
delete:DELETE
因此,举个简单的例子,您可以在 HAL-Json 中有一个 post 对象,如下所示:
{
"_links": {
"self": {
"href": "https://hostname/api/v1/posts/1",
"actions": {
"read": "GET",
"update": "PUT",
"delete": "DELETE"
}
}
},
"_embedded": {
"owner": {
"id": 1,
"name": "John Doe",
"email": "[email protected]",
"_links": {
"self": {
"href": "https://hostname/api/v1/users/1",
"actions": {
"read": "GET"
}
}
}
}
},
"subject": "Post subject",
"body": "Post message body"
}
现在,我们在前端所要做的就是使用一个方法来检查我们要执行的操作是否在 actions 对象中。AclService
isAllowed
目前在前端,它看起来很简单:post.isAllowed('delete');
我认为 REST 3 级很棒,但它可能会导致一些头痛。您需要对REST有很好的理解,如果您想使用3级REST,我建议严格遵循REST概念,否则在实现REST时很容易迷失方向。
在我们的例子中,我们的优势在于我们正在构建前端和后端,但原则上它不应该有所作为。 但我在我们团队中看到的一个常见陷阱是,一些开发人员试图通过改变他们的后端模型来解决前端问题(架构),使其“适合”前端需求。
评论