假设我有以下课程:
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(公司或用户)
答案 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适用于这两种情况。