正确的层次结构插入和更新的REST方法

时间:2016-03-16 12:41:51

标签: rest

假设我有以下课程:

company 
user
address

公司对象可以包含用户和地址, 用户对象包含地址

公司可以独立存在,但用户和地址始终是公司或用户的一部分(UML措辞中的组合):

company ->
          addresses
          users ->
                  addresses

现在我想为POST(插入)和PUT(更新)请求使用正确的REST结构。什么是正确的方法?

变体1:

// insert address:
POST /api/company/{companyId}/addresses
POST /api/company/{companyId}/users/{userId}/addresses
// update address:
PUT /api/company/{companyId}/addresses/{addressId}
PUT /api/company/{companyId}/users/{userId}/addresses/{addressId}

变体2:

// insert address:
POST /api/address // companyId, userId etc. as parameter
// update address:
PUT /api/address/{addressId}

我个人的直觉是将变体1用于创建(POST)和变体2用于更新(PUT),因为变体1看起来更清洁"用于创建但更新变体2会更好,因为它不需要parentId(公司或用户)

1 个答案:

答案 0 :(得分:4)

首先,REST是一种架构风格而非协议。这意味着您定义URI的方式没有对错。 Roy Fielding实施的唯一要求是每个资源必须具有唯一的资源标识符(URI)。您可以对这些URI(动词)执行的操作由底层超文本传输​​协议(HTTP)定义。

其次,有一些最佳实践,例如将子资源用于嵌入其他资源的资源(特别是如果它们不能存在,而不存在父资源),就像在addresses的情况下那样users / companies

由于您在更新子资源方面存在某些问题:

通常更新资源使用HTTP动词PUT,它具有将当前状态(所有可用数据)替换为您已发送到服务器的状态的限制(如果更新发生了好)。由于在HTTP中尚未定义部分更新,因此使用PUT动词有点模糊并仅更新请求中的可用内容,但发出PATCH请求并且仅更新这些字段可能更正确就HTTP规范而言。

子资源与常规资源非常相似。如果更新子资源,则不需要更新父资源。在您的特定情况下,如果您要更新用户的地址而不是用户本身,则向服务器发出包含新地址状态的PUT /users/{userId}/addresses/{addressId} HTTP/1.1请求。请求正文可能如下所示:

{
    "street": "Sample Street 1"
    "city": "Sampletown",
    "zip": "12345",
    "country": "Neverland",
    "_links": {
        "self": { "href": "/users/{userId}/addresses/{addressId} }
        "googleMap": { "href": "https://www.google.com/maps/place/Liberty+Island/@40.6883129,-74.042817,16.4z/data=!4m2!3m1!1s0x0000000000000000:0x005f3b2fa5c0821d" }
    }
}

如果要密切关注HTTP PUT动词并拥有可能在运行时出现的动态字段,则可能必须最终更改当前表定义,删除旧地址条目并插入新条目提供的信息(取决于您使用SQL与NoSQL的数据库层)。注意HTTP PUT动词语义!

在使用模糊更新策略(=部分更新)时,简单的更新语句应该没问题。在这里,您可以简单地忽略URI中包含的其他userId,尽管这不是(完全)HTTP兼容的 - 至少在规范方面。在这种特殊情况下,您可以选择您选择的URI版本,但版本2仅适用于后一种情况,而版本1适用于这两种情况。