当前位置: 首页 > 知识库问答 >
问题:

仅针对聚合根更新RESTful资源

朱通
2023-03-14

通常,我使用以下资源URI方案构建RESTful API:

POST /products
PATCH /products/{id}
GET /products
GET /products/{id}
DELETE /products/{id}

产品还可能包含产品功能。当我想获取一些产品的特性时,我会执行一个get/products/{id}/features。

顺便说一句,如果我想向给定产品添加新功能,通常我不会提供这样的资源URI:PATCH /products/{id}/特性,但我认为特性是给定产品的一部分,因此,我更新哪些特性可能包含以下特性:

PATCH /products/{id}

{
    "features": {
         "add": [1, 2, 3]
    }
}

另一方面,如果我想更新一些功能元数据,我不会使用产品资源,但我会执行如下请求:

PATCH /products/features/{id}

{
    title: "Test"
}

在我的例子中,产品功能与特定产品无关,但它们可以关联到许多产品。

理想情况下,我应该更新哪些功能拥有给定的产品,向产品/功能发出补丁请求,顺便说一句,这会使服务器API过于复杂,因为您需要单独覆盖所有实体的聚合。

我所关心的是,考虑某些给定聚合根的关联是否可以作为实体本身的一部分进行更新。

归根结底,人们可能会说像这样的API不是完全RESTful的,因为我不应该期望使用PATCH动词从某些给定产品中删除功能,而是DELETEDELETE /products/{id}/特性/{特性id},这使得从客户端的角度来看API的使用比使用DTO修补产品更容易。

共有1个答案

公良鸿风
2023-03-14

目前,您的体系结构并不是非常干净。在某种程度上,您实现了restful的思维方式,但另一方面

PATCH /products/{id}

{
    "features": {
         "add": [1, 2, 3]
    }
}

PATCH /products/features/{id}

{
    title: "Test"
}

是不直观的。

对于第一个例子,我建议

PATCH /products/{id}

{
    "features": [
        {
            "id": 1
        },
        {
            "id": 2
        },
        {
            "id": 3
        },
        {
            "id": 4
        }
    ]
}

您提供此产品的所有功能。您不能只定义自己的元素添加,这会将给定的功能添加到产品中。更新资源的结构应该与您使用get/products/{id}获得的结构相当(我猜您没有获得add属性,是吗?)。

对于第二个,您的url应该是类似于/产品特性/{feature\u id}或只是/特性/{feature\u id}。不要用产品/功能{feature\u id}打破模式。你为什么要这样想?这是合乎逻辑的-当您获取/产品时,您无法获取包含所有功能列表的资源

{
    ...
    "features": [
        {
             "id": 1
        },
        ...
    ]
    ...
}

而是所有产品的列表

{
     [
          {
               ....
               "id": 1,
               "features": ...
          },
          ...
     ]
}

回答您的问题,如果您按照我的建议以restful的方式正确地实现它,那么使用这种方法更新产品的特性是完全可以的。

编辑:

关于/产品/功能或/产品功能的事情,是否有共识?你知道有什么好的来源可以确保这不仅仅是一个口味的问题吗?

我认为这是误导。我希望获得所有产品中的所有功能,而不是获得所有可能的功能。但是,老实说,很难找到任何直接谈论这个问题的来源,但是有很多文章中,人们并不试图创建嵌套的资源,如/产品/功能,而是单独创建。

关于补丁时添加,这是我发现的最简单的表达方式,即我正在添加、更新或删除给定产品的功能。事实上,这是一个DTO

如果要对集合使用某些操作,则有一个标准。只需看看在REST Api调用中批量更新集合的最佳实践https://www.rfc-editor.org/rfc/rfc6902#appendix-A、 2。而不是

PATCH /products/{id}

{
    "features": {
         "add": [1, 2, 3]
    }
}

您将发送

PATCH /products/{id}

{
    "op": add, "path": "/features/0", "value": 1,
    "op": add, "path": "/features/0", "value": 2,
    "op": add, "path": "/features/0", "value": 3
}

顺便说一句,注意你没有回答我问题中的核心问题。

由于所有内容都可以被视为子资源,如果产品的功能(作为一个集合)是产品的一个属性,并且它被视为子资源来启用POST(例如),我看不到任何冲突。如果一个人发现它不安全,那么您可以将其剪切为仅在子资源上操作。

此外,在这篇文章中,作者批准了使用PATCH更新子资源的方法,简化了过程(整篇文章有点争议,但并不是我们感兴趣的事情)

另一种解决方案是公开要使其可编辑的资源属性,并使用PUT方法发送更新的值。在下面的示例中,暴露了用户123的电子邮件属性:

PUT /users/123/email
 
new.email@example.org

虽然它让事情变得清楚,而且看起来是一种很好的决定要公开什么和不公开什么的方法,但此解决方案给API带来了很多复杂性(控制器中的更多操作、路由定义、文档等)。然而,它符合REST,是一个不错的解决方案,但有一个更好的选择:补丁

 类似资料:
  • 我正在为一个提供临床数据分析的服务设计一个REST风格的API。API允许用户创建患者资源。此资源为服务器端分析提供输入数据。 创建患者既不安全,也不等幂(服务器分配ID),因此使用POST,对患者进行POST 患者资源可能很大,因此它有可以更新的子资源,例如药物。更新药物是幂等的,因为整套药物将被替换,因此使用PUT。 临床分析由请求后/患者/{patientId}/analysisResult

  • 我有一个KTable,数据如下所示(key=>value),其中keys是客户ID,而value是包含一些客户数据的小型JSON对象: 我想对这个KTable做一些聚合,基本上保留每个的记录数。所需的KTable数据如下所示: 假设属于上面的组,她的生日使她进入了新的年龄组。支持第一个KTable的状态存储现在应该如下所示: 我希望得到的聚合KTable结果反映这一点。例如。 我可能过度概括了这里

  • 问题内容: 我在SQL Server中运行合并。在我的更新中,我只想更新值已更改的行。版本行在每次更新时都会递增。下面是一个示例: 现在,如果我只想更新行,从而增加版本,则仅在名称更改的情况下。 问题答案: 可以有。另外,无需更新。 如果Last_Name或First_Name可为空,则例如在比较trg.Last_Name <> src.Last_Name时,需要注意值。

  • 问题内容: 我遇到了一个mongo问题。我想知道是否有办法做一个蒙戈控制台命令而不是多个以下和电话。 我想用这个新的数组项更新对象 我使用了此方法,它是有限的,如果它已经存在,它不会将一个项目插入数组,但不会基于标识符更新项目。在这种情况下,我真的很想根据来更新此条目。 这给了我: 当我真正想要的是: 问题答案: 您可以使用位置运算符执行此操作: 的在更新对象充当的第一个元素的占位符到查询选择器相

  • 抱歉,如果已经问过了,但一直潜伏在SO周围,找不到任何适合我需要的东西。 基本上,我在使用ES的第一次快速尝试中试图实现的是在术语聚合中添加更多计数器。 快速尝试一下,我将以下请求发送给ES。 我现在得到的只是样本在文档中显示的内容。 但是,我真的不知道如何在桶中包含更多的内部聚合。会导致这样的文档的东西。 我应该如何构造聚合,以便按桶包含这些聚合?

  • 我有两个集合A和B,我在另一个集合B中有一个字段的引用,因此将两个结果聚合到: 集合A中的文件是: