REST API:我们应该使用 PUT 还是 DELETE 来部分更新资源?
REST API: Should we use PUT or DELETE to update resource partially?
我们有像 pluralsight 这样的网站,作者和客户可以在上面注册。作者发布他们的课程,客户可以给这些课程打分。 table 结构如下:
作者table:(保存作者基本信息:一对一)
authorId |姓名 |联络 |电邮 |评分
1 |萨希尔 | 9971343992 |沙夫@gmail.com | 3.2
authorRating:(保存客户给作者的评分:一对多)
编号 |作者编号 |客户编号 |评分 |
1 | 1 | 101 | 2.7
2 | 1 | 201 | 3.7
作者 table 中的评分会在某些记录在 authorRating table 中获得 inserted/updated/deleted 时更新。有一些复杂的算法可以根据 authorRating table 记录确定 author table 的评分。
我们为此创建了以下 APIs:
PUT api/author/1/rating /:如果 authorRating table 有任何变化,我们将重新计算该作者的评级并触发此 API 通过新评级。这接受作者 table 中的评级和 add/update。如果作者 table 没有 id=1,它返回验证错误
DELETE api/author/1/rating /:这会删除作者 id=1 的评级,即将其设置为 NULL。如果作者 table 没有 id=1,它返回验证错误。
这是正确的 API 设计吗?或者我们应该只公开 PUT API,如果他们在 PUT API 中发送评级为 null,我们将在作者 table 中将其设置为 null ?
或者我们应该考虑在这里使用 PATCH 吗?
就你只修改一个结构的字段而言,我认为 PATCH 更适合这里,但它应该发送到父资源:
PATCH api/author/1
对 RESTful API 使用 POST 方法是一种常见的做法。您几乎可以 post 任何消息并通过参数处理消息。您可以 POST 删除、更新或其他命令,具体取决于您的需要。
对于这些评级操作,我会使用类似的东西:
- 要为作者 1 插入新评级,请使用
POST /api/author/1/rating
- 要更新作者 1 的评级,请使用
PATCH /api/author/1/rating
。您可能希望 authorRating
table 中有更多不想更改的数据(如作者和客户 ID),但您只是更新了一些字段,在本例中为评级。
- 要删除作者的 1 评分,
DELETE /api/author/1/rating
,正如您所解释的,是有道理的。
HTTP 是一种协议,它定义了允许操纵资源的方法;互联网上的文件或数据可以这么说。在操作这些资源时,由调用的操作之一触发的任何业务逻辑或多或少只是副作用。虽然某些事情可以通过多种方式实现,但操作在它们传达的语义上(略有)不同。
PUT
在 RFC 7231 中指定,它用请求中提供的表示替换当前表示,确实说明了有关部分更新的以下内容:
Partial content updates are possible by targeting a separately identified resource with state that overlaps a portion of the larger resource, or by using a different method that has been specifically defined for partial updates (for example, the PATCH method defined in RFC5789).
因此,您可以选择“重叠”资源并更新其他资源,这也会更改重叠数据,从而也更改实际资源中的数据,或者使用 PATCH
因此
入门者很容易想到其他资源的某些信息嵌入到实际资源中,并且在更新其他资源时,实际资源的状态也会因此发生变化。想想一个用户和他的地址,即
根据 Roy Fielding 的说法,他在论文中写了以下内容
The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author's hypertext reference must fit within the definition of a resource. A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.”
资源应通过自己的唯一标识符命名和引用。尽管将实体直接映射到资源通常是不可取的,因为资源可以而且可能应该包含更多信息,以便客户端采取进一步的行动。
因此,如果您认为评级是一个好的实体 and/or 一个好的资源,则取决于您。虽然这是一个固执己见的立场,但我不是很喜欢这个。
DELETE
有一些特殊的语义。它实际上并不能保证删除文件,尽管它会通过删除特定资源的关联(URI)使资源不可用。删除的资源如何处理实际上取决于实现。
The DELETE method requests that the origin server remove the association between the target resource and its current functionality. In effect, this method is similar to the rm command in UNIX: it expresses a deletion operation on the URI mapping of the origin server rather than an expectation that the previously associated information be deleted.
...
If the target resource has one or more current representations, they might or might not be destroyed by the origin server, and the associated storage might or might not be reclaimed, depending entirely on the nature of the resource and its implementation by the origin server (which are beyond the scope of this specification). ... In general, it is assumed that the origin server will only allow DELETE on resources for which it has a prescribed mechanism for accomplishing the deletion.
通常 DELETE
只应用于通过 PUT
或 POST
创建的资源,这些资源之前的创建是通过自己的 Location
响应 header.
话虽如此,正如您询问这是否是正确的 API 设计一样。其实,没有对错之分,你采取哪种立场主要是一种自以为是的立场。只要您遵守 HTTP 协议规范(在您的特定情况下),就不会违反 REST 架构师原则。如果您以一种使它们具有唯一可识别性的方式设计您的评级资源,您可以使用 delete 取消对作者的相应评级的引用(并可能从您的数据库中删除数据)或发送一个包含该评级资源新内容的放置请求到各自的端点。
不过请记住,服务器应该尽最大努力告诉客户端它可以采取什么下一步行动,而无需客户端获得一些关于您的 API 的 out-of-band 信息,否则您会将客户端耦合到您的 API,因此当您将来更改 API 时可能会导致问题。
我们有像 pluralsight 这样的网站,作者和客户可以在上面注册。作者发布他们的课程,客户可以给这些课程打分。 table 结构如下:
作者table:(保存作者基本信息:一对一)
authorId |姓名 |联络 |电邮 |评分
1 |萨希尔 | 9971343992 |沙夫@gmail.com | 3.2
authorRating:(保存客户给作者的评分:一对多)
编号 |作者编号 |客户编号 |评分 |
1 | 1 | 101 | 2.7
2 | 1 | 201 | 3.7
作者 table 中的评分会在某些记录在 authorRating table 中获得 inserted/updated/deleted 时更新。有一些复杂的算法可以根据 authorRating table 记录确定 author table 的评分。
我们为此创建了以下 APIs:
PUT api/author/1/rating /:如果 authorRating table 有任何变化,我们将重新计算该作者的评级并触发此 API 通过新评级。这接受作者 table 中的评级和 add/update。如果作者 table 没有 id=1,它返回验证错误
DELETE api/author/1/rating /:这会删除作者 id=1 的评级,即将其设置为 NULL。如果作者 table 没有 id=1,它返回验证错误。
这是正确的 API 设计吗?或者我们应该只公开 PUT API,如果他们在 PUT API 中发送评级为 null,我们将在作者 table 中将其设置为 null ?
或者我们应该考虑在这里使用 PATCH 吗?
就你只修改一个结构的字段而言,我认为 PATCH 更适合这里,但它应该发送到父资源:
PATCH api/author/1
对 RESTful API 使用 POST 方法是一种常见的做法。您几乎可以 post 任何消息并通过参数处理消息。您可以 POST 删除、更新或其他命令,具体取决于您的需要。
对于这些评级操作,我会使用类似的东西:
- 要为作者 1 插入新评级,请使用
POST /api/author/1/rating
- 要更新作者 1 的评级,请使用
PATCH /api/author/1/rating
。您可能希望authorRating
table 中有更多不想更改的数据(如作者和客户 ID),但您只是更新了一些字段,在本例中为评级。 - 要删除作者的 1 评分,
DELETE /api/author/1/rating
,正如您所解释的,是有道理的。
HTTP 是一种协议,它定义了允许操纵资源的方法;互联网上的文件或数据可以这么说。在操作这些资源时,由调用的操作之一触发的任何业务逻辑或多或少只是副作用。虽然某些事情可以通过多种方式实现,但操作在它们传达的语义上(略有)不同。
PUT
在 RFC 7231 中指定,它用请求中提供的表示替换当前表示,确实说明了有关部分更新的以下内容:
Partial content updates are possible by targeting a separately identified resource with state that overlaps a portion of the larger resource, or by using a different method that has been specifically defined for partial updates (for example, the PATCH method defined in RFC5789).
因此,您可以选择“重叠”资源并更新其他资源,这也会更改重叠数据,从而也更改实际资源中的数据,或者使用 PATCH
因此
入门者很容易想到其他资源的某些信息嵌入到实际资源中,并且在更新其他资源时,实际资源的状态也会因此发生变化。想想一个用户和他的地址,即
根据 Roy Fielding 的说法,他在论文中写了以下内容
The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"), a collection of other resources, a non-virtual object (e.g. a person), and so on. In other words, any concept that might be the target of an author's hypertext reference must fit within the definition of a resource. A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.”
资源应通过自己的唯一标识符命名和引用。尽管将实体直接映射到资源通常是不可取的,因为资源可以而且可能应该包含更多信息,以便客户端采取进一步的行动。
因此,如果您认为评级是一个好的实体 and/or 一个好的资源,则取决于您。虽然这是一个固执己见的立场,但我不是很喜欢这个。
DELETE
有一些特殊的语义。它实际上并不能保证删除文件,尽管它会通过删除特定资源的关联(URI)使资源不可用。删除的资源如何处理实际上取决于实现。
The DELETE method requests that the origin server remove the association between the target resource and its current functionality. In effect, this method is similar to the rm command in UNIX: it expresses a deletion operation on the URI mapping of the origin server rather than an expectation that the previously associated information be deleted.
...
If the target resource has one or more current representations, they might or might not be destroyed by the origin server, and the associated storage might or might not be reclaimed, depending entirely on the nature of the resource and its implementation by the origin server (which are beyond the scope of this specification). ... In general, it is assumed that the origin server will only allow DELETE on resources for which it has a prescribed mechanism for accomplishing the deletion.
通常 DELETE
只应用于通过 PUT
或 POST
创建的资源,这些资源之前的创建是通过自己的 Location
响应 header.
话虽如此,正如您询问这是否是正确的 API 设计一样。其实,没有对错之分,你采取哪种立场主要是一种自以为是的立场。只要您遵守 HTTP 协议规范(在您的特定情况下),就不会违反 REST 架构师原则。如果您以一种使它们具有唯一可识别性的方式设计您的评级资源,您可以使用 delete 取消对作者的相应评级的引用(并可能从您的数据库中删除数据)或发送一个包含该评级资源新内容的放置请求到各自的端点。
不过请记住,服务器应该尽最大努力告诉客户端它可以采取什么下一步行动,而无需客户端获得一些关于您的 API 的 out-of-band 信息,否则您会将客户端耦合到您的 API,因此当您将来更改 API 时可能会导致问题。