创建REST资源:组合还是分离?

创建REST资源:组合还是分离?,rest,restful-architecture,api-design,Rest,Restful Architecture,Api Design,假设我需要为客户创建一个装运,并且该装运需要一个地址。这两种资源都需要创建 在REST设计中,首选哪种方法?为什么 # One request that in-lines the address. POST /shipments { "shipment": { "customer_id": 1, "address": { "city": "Toronto", ... } } } vs 这只是我的意见。但我会允许这两种方法。要做到这

假设我需要为客户创建一个装运,并且该装运需要一个地址。这两种资源都需要创建

在REST设计中,首选哪种方法?为什么

# One request that in-lines the address.
POST /shipments    
{
  "shipment": {
    "customer_id": 1,
    "address": {
      "city": "Toronto",
      ...
    }
  }
}
vs


这只是我的意见。但我会允许这两种方法。要做到这一点,我将使用两种不同的媒体类型。但如果您只使用
application/json
media type,则仍然可以同时允许这两种类型

大多数语言都有从
json
到模型对象的
自动反序列化器。但是YMMV

尽管两种媒体类型的方法是可选的,并且具有巨大的影响(因为您需要对所有表示使用特定的媒体类型,而不是
application/json
),但我更喜欢显式的。它允许提前丢弃请求(头首先到达服务器和客户端)


但是,如果你问是否有任何“首选”方式,那就不是问题了。一个为您(或客户机)保存一个请求和一个网络往返,但另一个更清楚地说明了API的结构。

根据您的描述,您需要操作/管理两个“REST”资源:发货和地址

您还暗示该地址是正在装运的子资源

用例1:创建包含地址信息的装运

POST /shipments/
BODY: {address {<<address info}}
RETURN: {shipment-id}
POST /shipments/
BODY: {}
RETURN: {shipment-id}
PUT /shipments/[shipment-id]/address/
BODY: {address {<<address info}}
用例3:使用地址信息更新装运

POST /shipments/
BODY: {address {<<address info}}
RETURN: {shipment-id}
POST /shipments/
BODY: {}
RETURN: {shipment-id}
PUT /shipments/[shipment-id]/address/
BODY: {address {<<address info}}
用例5:您可以使用地址id更新发货

PUT /shipments/[shipment-id]/address/
BODY: {address–id {id-} }

首先考虑业务和可用性的影响,然后使用RESTful设计实践来实现合理的解决方案,这一点很重要

在您的示例中,如果您仅实现单独的REST调用来创建发货和嵌入地址,那么应用程序开发人员将无法以原子方式创建发货。如果发货创建成功,然后地址创建失败或网络连接丢失,则您的后端数据不完整。即使应用程序开发人员能够回滚更改,您也为应用程序设计增加了巨大的复杂性


因此,对于创建,我会允许将地址包括在装运中,或者如果没有地址的装运违反了您的业务规则,则会强制将其包括在内。其他决策(例如,是创建单独的更新调用,还是同时更新所有装运字段,或者同时支持这两个字段)通常也会由业务规则和应用程序设计提出。

我相信任何现代公共API(假设您的API是公共的)设计应该来自最常见的用例,而不是代码的内部结构。也就是说,您的API应该尽可能适用于该用例。可测试性和内部结构也很重要,但可用性更重要,因为开发人员(实际上是您服务的客户)更关心API的易用性,而不是内部设计的复杂性

所以,我认为,除非您为您的API提供一组最常见的用例,否则不可能为您的问题提供特定的答案。然后,您应该考虑询问对API一无所知的开发人员如何完成用例。最常见的答案将显示最佳方法