《 REST在实践中:超媒体和系统体系结构 》一书以一家虚构的StarBucks公司作为运行示例。
我认为这是一个很好的例子,因为大多数人都熟悉该领域。
设计也很简单,可以遵循,但是足够复杂,很有趣。
问题域
RESTbucks是关于订购咖啡(或茶)和食物并为其付款的。 这是客户端的状态图:
- 创建订单
- 更新订单
- 取消订单
- 支付订单
- 等待订单准备
- 接受订单
书本设计
本书中针对该服务的超媒体设计如下:
- 客户端
POST
向众所周知的RESTBucks URI发出命令。 这将在Location
标头中返回订单URI。 然后,客户端GET
订单URI - 客户端
POST
将订单更新为订单URI - 客户端
DELETE
的订单URI - 客户端
PUT
SA支付给URI找到通过查找相关链接http://relations.restbucks.com/payment
- 客户端
GET
订单URI,直到状态更改 - 客户端
DELETE
是通过查找具有以下关系的链接http://relations.restbucks.com/receipt
找到的URI。
本书使用专门的媒体类型application/vnd.restbucks.order+xml
交换所有消息。
设计问题
这是我使用上述方法遇到的一些问题:
- 我认为该服务的知名URI( Mike Amundsen称为广告牌URI )应响应
GET
,以便客户可以安全地进行浏览。
这增加了一条额外的消息,但也使它可以通过附加功能扩展服务。 例如,在本书的下一章中添加菜单时,将引入第二个众所周知的URI。 如果在订购服务之前有适当的类似于家庭文档的资源,则可以将其限制为新的链接关系。 - 我宁愿使用
PUT
更新订单,因为这是幂等方法。 该书指出GET
返回的表示形式包含链接,并认为这意味着(1)PUT
消息也应包含那些链接,以及(2)由于这些链接在服务器的控制之下,这很奇怪。
我不同意这两种说法。 服务器不一定必须使GET
和PUT
的格式完全相同。 即使这样做,链接等某些部分也可能是可选的。 此外,服务器没有理由不接受和忽略链接。 -
DELETE
很好。
一种替代方法是使用状态为canceled
PUT
,因为无论如何我们已经具有status属性。 这带来了更多的可能性,例如重新设置已取消的订单,还引入了诸如垃圾回收之类的问题。 - 我不认为
PUT
是正确的方法。 这项服务真的可以保证在所有情况下重复付款后我的信用卡都不会被扣款吗?
更重要的是,这种设计假设付款始终是针对整个订单的。 乍一看似乎是合乎逻辑的,但是一旦这本书介绍了逻辑消失的凭证。 如果我有一张免费咖啡的优惠券,那么我仍然要为吃的东西或第二杯咖啡付费。 我将创建客户应过POST
到的付款的集合。 我还将使用RFC 5988中定义的标准payment
链接关系。 - 这可以。
- 这对我来说毫无意义:下订单与删除收据不同。 我在出差时需要收据,所以我可以报销!
我宁愿PUT
与地位的新秩序taken
。
服务演进
假设您已经根据书中的设计实现了自己的服务。
在不破坏任何潜在客户的情况下,您如何做到这一点?
毕竟,难道不是什么支持者吹捧为RESTful方法的优势之一?
好,是的,不是。 本书中定义的媒体类型为3a级 ,因此您可以更改URI。 但是,HTTP方法的使用是带外定义的,您不能轻易更改它。
现在想象一下,这本书将使用完整的超媒体类型(3b级)。 在这种情况下,使用的HTTP方法将成为消息的一部分。 客户端将不得不发现它,而服务器可以轻松地对其进行更改,而不必担心破坏客户端。
当然,这是以必须在客户端中构建更多逻辑为代价的。 这就是为什么我认为使用现有的完整超媒体类型(例如Mason , Siren或UBER)是一个好主意的原因。 此类通用媒体类型很可能随库一起提供,这些库将为客户端处理此类工作。
翻译自: https://www.javacodegeeks.com/2014/06/restbucks-evolved.html