springhateoas
是的,我知道这个标题听起来很愚蠢,但是找不到更合适的名称。 因此,让我解释一下为什么我认为HATEOAS API中的链接并不总是那么有用。
如果您不知道HATEOAS是什么,建议您先阅读《超媒体REST API简介》 。
具有HATEOAS支持的REST API提供了两个用于将客户端和服务器分离的主要功能:
- 超媒体避免了客户端需要硬编码和构造URI。 这有助于服务器在将来发展REST-API。
- 链接的可用性告诉客户端可以对资源执行哪些操作。 这样可以避免服务器逻辑需要在客户端上重复。例如,假设客户需要决定是否在订单旁边显示付款按钮。 这样做的逻辑可能是:
if (order.status == OPEN and order.paymentDate == null) { show payment button }
使用HATEOAS,客户无需了解此逻辑。 支票变成:
if (order.links.getByRel("payment") != null) { show payment button }
服务器现在可以更改规则,该规则决定何时可以付款,而无需客户端更新。
这些功能的有用程度取决于您的应用程序,系统架构和客户端。
对于大多数使用CRUD操作的应用程序,第二点可能没什么大不了的。 但是,如果您的REST API服务于更复杂的域,则它可能非常有用。
第一点取决于您的客户,并在一定程度上取决于您的整体系统架构。 如果为公共客户端提供API,则至少某些客户端很有可能会硬编码请求URI,而不会使用您提供的链接。 在这种情况下,您会失去发展API的能力而不会破坏(至少一些)客户端。
如果您的客户不直接使用您的API响应,而是公开自己的API,那么他们也不太可能跟随您返回的链接。 例如,在使用Backend for Frontend模式时很容易发生这种情况。
考虑以下示例系统体系结构:
后端服务由其他两个系统使用。 两种系统都提供与系统特定后端通信的用户界面。 REST用于所有通信。
假设用户使用Android应用程序(1)执行操作。 该应用程序向移动后端(2)发送请求。 然后,移动后端可以与后端服务(3)通信以执行请求的操作。 在将响应发送回Anroid-App之前,移动后端还可以预处理,映射或聚合从后端服务检索到的数据。
现在回到HATEOAS。
如果此示例体系结构中的后端服务(3)提供了Hypermedia REST API,则客户端几乎无法利用HATEOAS相关的链接。
让我们看一下显示系统通信的序列图以查看问题:
后端服务(3)提供一个API入口点,该入口返回所有可用操作及其请求URI的列表。 移动后端(2)定期向该API入口点发送请求,并在本地缓存链接列表。
现在,假设Android-App(1)的用户想要访问特定的订单。 为了检索所需的信息,Anroid-App向移动后端(2)发送请求。 该请求的URI可能先前已从Mobile-Backends API入口点(未显示)中检索到。
为了从后端服务检索请求的订单,移动后端使用缓存链接列表中的订单详细信息链接。 后端服务返回带有HATEOAS链接的响应。 在此,订单付款链接指示可以付款。 现在,移动后端将响应转换为自己的返回格式,并将其发送回Android-App。
移动后端也可能返回HATEOAS响应。 因此,需要将来自后端服务的链接URI映射到适当的移动后端URI。 因此,Mobile-Backend会检查Backend-Service响应中是否存在订单付款链接。 如果是这种情况,它将在自己的响应中添加一个订单付款链接。
请注意,Mobile-Backend仅使用Backend-Service响应的关系( rel字段)。 URI被丢弃。
现在,用户想要支付订单。 Android-App使用先前检索到的订单付款链接将请求发送到移动后端。 现在,移动后端已失去上一个后端服务响应的上下文。 因此,它必须在缓存的链接列表中查找订单付款链接。 该过程以与上一个请求相同的方式继续
在此示例中,Android-App能够利用HATEOAS相关链接。 但是,Mobile-Backend无法使用Backend-Service响应返回的链接URI(API入口点除外)。 如果移动后端正在提供HATEOAS功能,则后端服务的链接关系可能会很有用。 始终从缓存的API入口点响应中查找后端服务请求的URI。
交流动作而不是链接
不幸的是,链接的构建并不总是那么简单,可能会花费一些额外的时间。 如果您知道客户不会使用这些链接,那么这将是浪费时间。
避免客户端上的逻辑重复的最简单方法是忽略链接,并在REST响应中使用简单的动作数组:
GET /orders/123
{
"id": 123,
"price": "$41.24 USD"
"status": "open",
"paymentDate": null,
"items": [
...
]
"actions": ["order-cancel", "order-payment", "order-update"]
}
这样,我们无需构造链接就可以传达可能的动作。 在这种情况下,响应告诉我们客户可以执行取消,付款和更新操作。
请注意,这甚至可能不会增加客户端和服务器之间的耦合。 客户端仍然可以在API入口点中查找那些动作的URI,而无需对URI进行硬编码。
一种替代方法是使用标准链接元素,而只是跳过href属性:
GET /orders/123
{
"id": 123,
"price": "$41.24 USD"
"status": "open",
"paymentDate": null,
"items": [
...
]
"links": [
{ "rel": "order-cancel" },
{ "rel": "order-payment" },
{ "rel": "order-update" },
]
}
但是,返回没有链接URI的links元素可能会有些混乱。
显然,您将通过两种描述的方法离开标准路径。 另一方面,如果不需要链接,则可能也不想使用标准化的HATEOAS响应格式(例如HAL )。
翻译自: https://www.javacodegeeks.com/2020/12/hateoas-without-links.html
springhateoas