RESTful,hypertext-driven系统需要使客户端能够创建依赖于三种或更多不同类型资源的新资源。揭示这种能力的最佳方法是什么?
举个例子,假设我经营一家在线商店。服务器知道四种资源:
订单发货时,客户需要通过在服务器上创建新货件来记录此事件。货件将需要参考目的地,订单和包装工具。
为了实现新货的创建,我可以想到三种方法,我不喜欢它们中的任何一种:
我不喜欢选项1,因为Shipment媒体类型还定义了Order,Packer和Destination的链接。这里,“链接”是由人类可读的名称,URI和媒体类型组成的JSON哈希。将“order_uri”,“packer_uri”和“destination_uri”添加到媒体类型似乎不是很干,因为它复制了相关资源的URI。
选项2使用深层嵌套的URI,它们看起来既不易维护也不捕获任何有意义的分层信息。
选项3在客户端和发货的创建之间设置了另一层抽象,这使得系统更难学习。
如果货件仅依赖于另一个资源,则选项2会更有意义,但在这种情况下不会。就目前而言,我赞成选项3,但更喜欢更好的东西。
在此示例中,创建新货件的URI和媒体类型的最佳组合是什么?还应考虑其他哪些方法?
更新:下面是发货资源的JSON示例表示,显示订单,打包程序和目标的链接。选项1所需的URI重复出现在“货件”散列中:
{
"shipment":{
"created_at": "Wed Sep 09 18:38:31 -0700 2009",
"order_uri":"http://example.com/orders/815",
"packer_uri":"http://example.com/packers/42",
"destination_uri":"http://example.com/destinations/666"
},
"order":{
"name":"the order to which this shipment belongs",
"uri":"http://example.com/orders/815",
"media_type":"application/vnd.com.example.store.Order+json"
},
"packer":{
"name":"the person who packed this shipment",
"uri":"http://example.com/packers/42",
"media_type":"application/vnd.com.example.store.Packer+json"
},
"destination":{
"name":"the destination of this shipment",
"uri":"http://example.com/destinations/666",
"media_type":"application/vnd.com.example.store.Destination+json"
}
}
“发货”哈希的内容(减去“created_at”字段)将被POST。使用GET时,将发送上面的完整发货表示。
答案 0 :(得分:2)
REST“层次结构”不是意味着任何东西。它们便于导航以路径的形式显示关系。不是层次结构本身,而是路径。因此,如果您放弃“层次结构”概念并认识到存在许多到同一最终位置的替代路径,则选项2实际上是明智的。
您的选项2是order-> packers->目标路径。从理论上讲,订单 - >目的地 - >包装商和包装商 - >订单 - >目的地,包装商 - >目的地 - >订单,以及其他一些订单都在同一个地方。是的,支持他们所有人都很痛苦。但是,它证明所有这些都是等效的和没有层次结构。
“我不喜欢选项1,因为[它]似乎不太干。”
所以?省略重复的东西。为什么货件还必须包含订单和包装商信息的完整重复? URI引用足以允许查找和检索Order和Packer。为什么要发送Order and Packer?
“选项3使系统更难学习。”为了谁?开发商?您是围绕开发人员而不是用户及其用例设计系统的?耻辱。
REST的观点是(通常)URI是绝对的,最终的和永恒的。哪种替代方案为您提供绝对最佳的URI结构?认识到URI是不是层次结构,但路径 - 和对象可以存在于多个备用路径的末尾。
您正在创建货件。发布到/shipment
。简单明了的URI就是重要的。
答案 1 :(得分:1)
好的,现在我明白你在哪里看到重复了。发布以下内容是否可行?
{
"shipment":{
"created_at": "Wed Sep 09 18:38:31 -0700 2009",
"order":{
"uri":"http://example.com/orders/815"
},
"packer":{
"uri":"http://example.com/packers/42",
}
"destination":{
"uri":"http://example.com/destinations/666",
}
}
}
并返回此
{
"shipment":{
"created_at": "Wed Sep 09 18:38:31 -0700 2009",
"order":{
"name":"the order to which this shipment belongs",
"uri":"http://example.com/orders/815",
"media_type":"application/vnd.com.example.store.Order+json"
},
"packer":{
"name":"the person who packed this shipment",
"uri":"http://example.com/packers/42",
"media_type":"application/vnd.com.example.store.Packer+json"
},
"destination":{
"name":"the destination of this shipment",
"uri":"http://example.com/destinations/666",
"media_type":"application/vnd.com.example.store.Destination+json"
}
}
}
也许这只是在JSON中不起作用,但我在我的资源中使用类似的XML。我们的想法是,只需填写uri就可以向服务器传递一个资源的“引用”,服务器会填充对象中的其余数据。
答案 2 :(得分:0)
我认为option1和option2是公平的解决方案,我会忘记option3,因为之前的解决方案是更好的解决方案。
您的客户端应始终通过检查链接的语义(例如链接关系和供应商特定的MIME类型)而不是通过检查URL结构来决定。您不一定需要供应商特定的MIME类型,您可以使用和RDF格式,如JSON-LD和REST以及特定于应用程序的词汇来描述您的链接及其输入字段,您可以使用例如Hydra。您也可以使用自定义解决方案,例如将_fields添加到_links。
重复链接没有错。如果邮件大小太大,您可以使用gzip。顺便说一句,你不应该将URL与链接混淆,它们是不同的东西。 URL是资源标识符,链接是对资源的可能操作调用。