REST - 是否将 ID 放在正文中?
REST - put IDs in body or not?
假设我想为人们提供一个 RESTful 资源,客户端可以在其中分配 ID。
一个人长这样:{"id": <UUID>, "name": "Jimmy"}
现在,客户端应该如何保存(或"PUT")呢?
PUT /person/UUID {"id": <UUID>, "name": "Jimmy"}
- 现在我们有这个讨厌的重复,我们必须一直验证:正文中的 ID 是否与路径中的 ID 匹配?
- 不对称表示:
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID
returns {"id": <UUID>, "name": "Jimmy"}
- 正文中没有 ID - 仅在位置中有 ID:
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID
returns {"name": "Jimmy"}
POST
似乎不是个好主意,因为 ID 是由客户端生成的。
常见的模式和解决方法是什么? IDs only in location 似乎是最教条的正确方式,但它也使实际实施更加困难。
您可能需要查看 PATCH/PUT 请求类型。
PATCH 请求用于部分更新资源,而在 PUT 请求中,您必须将整个资源发送到它在服务器上被覆盖的位置。
就 url 中的 ID 而言,我认为您应该始终拥有它,因为这是识别资源的标准做法。即使是条纹 API 也是如此。
您可以使用 PATCH 请求更新服务器上带有 ID 的资源以识别它,但不要更新实际 ID。
拥有不同的 read/write 模型并没有错:客户端可以编写一个资源表示,然后服务器可以 return 另一种包含 added/calculated 元素的表示(甚至完全不同的表示 - 任何规范都没有反对它,唯一的要求是 PUT 应该创建或替换资源)。
所以我会选择 (2) 中的非对称解决方案,并在编写时避免服务器端的 "nasty duplication check":
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
此问题的一个解决方案涉及 "Hypertext As The Engine Of Application State," 或 "HATEOAS." 的概念,这意味着 REST 响应包含可用资源或要作为超链接执行的操作。使用这种方法,这是 REST 最初概念的一部分,资源的唯一 identifiers/IDs 本身就是超链接。所以,例如,你可以有这样的东西:
GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}
然后,如果你想更新那个资源,你可以这样做(伪代码):
updatedPerson = person.data
updatedPerson.name = "Timmy"
PUT(URI: response.resource, data: updatedPerson)
这样做的一个优点是客户端不必了解服务器对用户 ID 的内部表示。 ID 可以改变,甚至 URL 本身也可以改变,只要客户端有办法发现它们。例如,当获取一组人时,您可以 return 像这样的响应:
GET /people
{ "people": [
"/person/1",
"/person/2"
]
}
(当然,您也可以 return 每个人的完整人物对象,具体取决于应用程序的需要)。
使用这种方法,您可以更多地从资源和位置的角度考虑您的对象,而不是从 ID 的角度考虑对象。因此,唯一标识符的内部表示与您的客户端逻辑分离。这是 REST 背后的最初动力:通过使用 HTTP 的特性,创建比以前存在的 RPC 系统耦合更松散的客户端-服务器架构。有关 HATEOAS 的更多信息,请查看 Wikipedia article as well as this short article.
这个问题之前有人问过-讨论值得一看:
Should a RESTful GET response return a resource's ID?
这是很容易陷入围绕 what is and is not "RESTful" 争论的问题之一。
对于它的价值,我试着从一致的资源的角度来思考,而不是在方法之间改变它们的设计。然而,恕我直言,从可用性的角度来看,最重要的是你在整个 API!
中保持一致
我正在从 JSON-LD/ Semantic Web point of view because that's a good way to go to achieve real REST conformance as I have outlined in these slides 看这个。从这个角度来看,毫无疑问选择选项 (1.),因为 Web 资源的 ID (IRI) 应该始终等于 URL,我可以用它来查找/取消引用资源。
我认为验证并不难实现,计算强度也不高;所以我不认为这是选择选项 (2.) 的正当理由。
我认为选项 (3.) 并不是真正的选项,因为 POST(创建新的)与 PUT(更新/替换)具有不同的语义。
使用不同的方法没有什么不好。但我认为最好的方法是 with 2nd.
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
它主要以这种方式使用甚至 entity framework 也使用这种技术 当实体被添加到 dbContext 中 class 没有生成的 ID 是通过 Entity Framework 中的引用生成的 ID。
在插入中,您不需要在 URL 中添加 id。这样,如果您在 PUT 中发送 ID,您可能会被解释为更新以更改主键。
插入:
PUT /persons/
{"id": 1, "name": "Jimmy"}
HTTP/1.1 201 Created
{"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
GET /persons/1
HTTP/1.1 200 OK
{"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
更新
PUT /persons/1
{"id": "2", "name": "Jimmy Jr"} -
HTTP/1.1 200 OK
{"id": "2", "name": "Jimmy Jr", "other_field"="filled_by_server"}
GET /persons/2
HTTP/1.1 200 OK
{"id": "2", "name": "Jimmy Jr", "other_field"="updated_by_server"}
JSON API 使用此标准并解决了将带有 link 的插入或更新对象返回到新对象的一些问题。某些更新或插入可能包含一些会更改其他字段的业务逻辑
你也会看到插入和更新后可以避免get。
如果是publicAPI你回复的时候要保守一点,大方地接受
我的意思是,您应该同时支持 1 和 2。我同意 3 没有意义。
同时支持 1 和 2 的方法是从 url 中获取 id,如果在请求正文中提供了 none,如果它在请求正文中,则验证它它匹配 url 中的 ID。如果两者不匹配,则 return 400 Bad Request 响应。
当return使用人员资源时要保守,并且始终在 json 中包含 id,即使它在 put 中是可选的。
虽然可以对不同的操作使用不同的表示形式,但 PUT 的一般建议是包含整个有效载荷。这意味着 id
也应该在那里。否则,您应该使用 PATCH。
话虽如此,我认为 PUT 应该主要用于更新,并且 id
也应该始终在 URL 中传递。因此,使用 PUT 更新资源标识符不是一个好主意。
当 URL 中的 id
可能与 body 中的 id
不同时,这让我们陷入了一种不受欢迎的境地。
那么,我们如何解决这样的冲突呢?我们基本上有 2 个选项:
- 抛出 4XX 异常
- 添加一个
Warning
(X-API-Warn
等) header.
我已经可以回答这个问题了,因为这个话题通常是一个见仁见智的问题。
仅供参考,这里的答案是错误的。
TLDR;如果你使用 PUT,你应该在正文中有 id。如果你使用的是 PATCH,你不需要在正文中使用 id。
参见:
https://restfulapi.net/rest-api-design-tutorial-with-example/
https://restfulapi.net/rest-put-vs-post/
https://restfulapi.net/http-methods/#patch
PUT
Use PUT APIs primarily to update existing resource (if the
resource does not exist, then API may decide to create a new resource
or not). If a new resource has been created by the PUT API, the origin
server MUST inform the user agent via the HTTP response code 201
(Created) response and if an existing resource is modified, either the
200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate
successful completion of the request.
If the request passes through a cache and the Request-URI identifies
one or more currently cached entities, those entries SHOULD be treated
as stale. Responses to this method are not cacheable.
Use PUT when you want to modify a singular resource which is already a
part of resources collection. PUT replaces the resource in its
entirety. Use PATCH if request updates part of the resource.
补丁
HTTP PATCH requests are to make partial update on a resource. If you
see PUT requests also modify a resource entity so to make more clear –
PATCH method is the correct choice for partially updating an existing
resource and PUT should only be used if you’re replacing a resource in
its entirety.
所以你应该这样使用它:
POST /device-management/devices : Create a new device
PUT /device-management/devices/{id} : Update the device information identified by "id"
PATCH /device-management/devices/{id} : Partial-update the device information identified by "id"
RESTful 实践表明,您在 /{id} 中 PUT 什么并不重要——记录的内容应该更新为有效负载提供的内容——但是 GET /{id } 应该仍然 link 到相同的资源。
换句话说,PUT /3 可能会将负载 ID 更新为 4,但 GET /3 仍应 link 到相同的负载(并且 return 将 ID 设置为 4 的负载) .
如果您决定 API 在 URI 和负载中需要相同的标识符,您的工作就是确保它们匹配,但如果您排除应该完整存在的有效载荷。这是接受的答案出错的地方。 PUT 必须替换整个资源,其中补丁可能是部分的。
假设我想为人们提供一个 RESTful 资源,客户端可以在其中分配 ID。
一个人长这样:{"id": <UUID>, "name": "Jimmy"}
现在,客户端应该如何保存(或"PUT")呢?
PUT /person/UUID {"id": <UUID>, "name": "Jimmy"}
- 现在我们有这个讨厌的重复,我们必须一直验证:正文中的 ID 是否与路径中的 ID 匹配?- 不对称表示:
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID
returns{"id": <UUID>, "name": "Jimmy"}
- 正文中没有 ID - 仅在位置中有 ID:
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID
returns{"name": "Jimmy"}
POST
似乎不是个好主意,因为 ID 是由客户端生成的。
常见的模式和解决方法是什么? IDs only in location 似乎是最教条的正确方式,但它也使实际实施更加困难。
您可能需要查看 PATCH/PUT 请求类型。
PATCH 请求用于部分更新资源,而在 PUT 请求中,您必须将整个资源发送到它在服务器上被覆盖的位置。
就 url 中的 ID 而言,我认为您应该始终拥有它,因为这是识别资源的标准做法。即使是条纹 API 也是如此。
您可以使用 PATCH 请求更新服务器上带有 ID 的资源以识别它,但不要更新实际 ID。
拥有不同的 read/write 模型并没有错:客户端可以编写一个资源表示,然后服务器可以 return 另一种包含 added/calculated 元素的表示(甚至完全不同的表示 - 任何规范都没有反对它,唯一的要求是 PUT 应该创建或替换资源)。
所以我会选择 (2) 中的非对称解决方案,并在编写时避免服务器端的 "nasty duplication check":
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
此问题的一个解决方案涉及 "Hypertext As The Engine Of Application State," 或 "HATEOAS." 的概念,这意味着 REST 响应包含可用资源或要作为超链接执行的操作。使用这种方法,这是 REST 最初概念的一部分,资源的唯一 identifiers/IDs 本身就是超链接。所以,例如,你可以有这样的东西:
GET /person/<UUID> {"person": {"location": "/person/<UUID>", "data": { "name": "Jimmy"}}}
然后,如果你想更新那个资源,你可以这样做(伪代码):
updatedPerson = person.data
updatedPerson.name = "Timmy"
PUT(URI: response.resource, data: updatedPerson)
这样做的一个优点是客户端不必了解服务器对用户 ID 的内部表示。 ID 可以改变,甚至 URL 本身也可以改变,只要客户端有办法发现它们。例如,当获取一组人时,您可以 return 像这样的响应:
GET /people
{ "people": [
"/person/1",
"/person/2"
]
}
(当然,您也可以 return 每个人的完整人物对象,具体取决于应用程序的需要)。
使用这种方法,您可以更多地从资源和位置的角度考虑您的对象,而不是从 ID 的角度考虑对象。因此,唯一标识符的内部表示与您的客户端逻辑分离。这是 REST 背后的最初动力:通过使用 HTTP 的特性,创建比以前存在的 RPC 系统耦合更松散的客户端-服务器架构。有关 HATEOAS 的更多信息,请查看 Wikipedia article as well as this short article.
这个问题之前有人问过-讨论值得一看:
Should a RESTful GET response return a resource's ID?
这是很容易陷入围绕 what is and is not "RESTful" 争论的问题之一。
对于它的价值,我试着从一致的资源的角度来思考,而不是在方法之间改变它们的设计。然而,恕我直言,从可用性的角度来看,最重要的是你在整个 API!
中保持一致我正在从 JSON-LD/ Semantic Web point of view because that's a good way to go to achieve real REST conformance as I have outlined in these slides 看这个。从这个角度来看,毫无疑问选择选项 (1.),因为 Web 资源的 ID (IRI) 应该始终等于 URL,我可以用它来查找/取消引用资源。 我认为验证并不难实现,计算强度也不高;所以我不认为这是选择选项 (2.) 的正当理由。 我认为选项 (3.) 并不是真正的选项,因为 POST(创建新的)与 PUT(更新/替换)具有不同的语义。
使用不同的方法没有什么不好。但我认为最好的方法是 with 2nd.
PUT /person/UUID {"name": "Jimmy"}
GET /person/UUID returns {"id": <UUID>, "name": "Jimmy"}
它主要以这种方式使用甚至 entity framework 也使用这种技术 当实体被添加到 dbContext 中 class 没有生成的 ID 是通过 Entity Framework 中的引用生成的 ID。
在插入中,您不需要在 URL 中添加 id。这样,如果您在 PUT 中发送 ID,您可能会被解释为更新以更改主键。
插入:
PUT /persons/ {"id": 1, "name": "Jimmy"} HTTP/1.1 201 Created {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"} GET /persons/1 HTTP/1.1 200 OK {"id": 1, "name": "Jimmy", "other_field"="filled_by_server"}
更新
PUT /persons/1 {"id": "2", "name": "Jimmy Jr"} - HTTP/1.1 200 OK {"id": "2", "name": "Jimmy Jr", "other_field"="filled_by_server"} GET /persons/2 HTTP/1.1 200 OK {"id": "2", "name": "Jimmy Jr", "other_field"="updated_by_server"}
JSON API 使用此标准并解决了将带有 link 的插入或更新对象返回到新对象的一些问题。某些更新或插入可能包含一些会更改其他字段的业务逻辑
你也会看到插入和更新后可以避免get。
如果是publicAPI你回复的时候要保守一点,大方地接受
我的意思是,您应该同时支持 1 和 2。我同意 3 没有意义。
同时支持 1 和 2 的方法是从 url 中获取 id,如果在请求正文中提供了 none,如果它在请求正文中,则验证它它匹配 url 中的 ID。如果两者不匹配,则 return 400 Bad Request 响应。
当return使用人员资源时要保守,并且始终在 json 中包含 id,即使它在 put 中是可选的。
虽然可以对不同的操作使用不同的表示形式,但 PUT 的一般建议是包含整个有效载荷。这意味着 id
也应该在那里。否则,您应该使用 PATCH。
话虽如此,我认为 PUT 应该主要用于更新,并且 id
也应该始终在 URL 中传递。因此,使用 PUT 更新资源标识符不是一个好主意。
当 URL 中的 id
可能与 body 中的 id
不同时,这让我们陷入了一种不受欢迎的境地。
那么,我们如何解决这样的冲突呢?我们基本上有 2 个选项:
- 抛出 4XX 异常
- 添加一个
Warning
(X-API-Warn
等) header.
我已经可以回答这个问题了,因为这个话题通常是一个见仁见智的问题。
仅供参考,这里的答案是错误的。
TLDR;如果你使用 PUT,你应该在正文中有 id。如果你使用的是 PATCH,你不需要在正文中使用 id。
参见:
https://restfulapi.net/rest-api-design-tutorial-with-example/
https://restfulapi.net/rest-put-vs-post/
https://restfulapi.net/http-methods/#patch
PUT
Use PUT APIs primarily to update existing resource (if the resource does not exist, then API may decide to create a new resource or not). If a new resource has been created by the PUT API, the origin server MUST inform the user agent via the HTTP response code 201 (Created) response and if an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to indicate successful completion of the request.
If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those entries SHOULD be treated as stale. Responses to this method are not cacheable.
Use PUT when you want to modify a singular resource which is already a part of resources collection. PUT replaces the resource in its entirety. Use PATCH if request updates part of the resource.
补丁
HTTP PATCH requests are to make partial update on a resource. If you see PUT requests also modify a resource entity so to make more clear – PATCH method is the correct choice for partially updating an existing resource and PUT should only be used if you’re replacing a resource in its entirety.
所以你应该这样使用它:
POST /device-management/devices : Create a new device
PUT /device-management/devices/{id} : Update the device information identified by "id"
PATCH /device-management/devices/{id} : Partial-update the device information identified by "id"
RESTful 实践表明,您在 /{id} 中 PUT 什么并不重要——记录的内容应该更新为有效负载提供的内容——但是 GET /{id } 应该仍然 link 到相同的资源。
换句话说,PUT /3 可能会将负载 ID 更新为 4,但 GET /3 仍应 link 到相同的负载(并且 return 将 ID 设置为 4 的负载) .
如果您决定 API 在 URI 和负载中需要相同的标识符,您的工作就是确保它们匹配,但如果您排除应该完整存在的有效载荷。这是接受的答案出错的地方。 PUT 必须替换整个资源,其中补丁可能是部分的。