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

通过REST API处理ManyToOne实体集合的更新

姜阳
2023-03-14

我正在努力思考如何通过RESTAPI将一个集合更新到另一个资源,并寻求其他人如何看待这个过程的指导。

假设您与实体“父”(一)和“子”(多)有多对一的关系。我的思考过程是,您可以通过单个PUT终结点处理更新父级的子级集合。这样,用于更新父实体的子实体和向父实体集合添加新子实体的终结点通过单个终结点进行。请求正文将包含一个子实体数组,endpoint本身将包含足够的信息来知道哪个父实体正在更新:

i、 放/父项/{uid}/子项

endpoint会告诉我们uid为{uid}的父实体是被请求的实体,并更新它的子实体。

这个机制感觉有点奇怪。也就是说,我必须以一种方式保留新实体,并以另一种方式更新它们。我的更新/保存操作最好以批处理的方式执行,但同时执行批处理保存和更新会让人感到奇怪。我必须同时执行这两项操作,因为您无法更新新实体

有没有更好的方法来实现这一点?这种关系是分层的,这意味着如果没有父级,子资源就不存在。我仍然希望能够批量发布/发布。

我可以公开POST与PUT的区别(使用与上面相同的endpoint)。我有一个约束,即子实体有一个唯一的名称,因此POST必须为具有现有名称的新子实体发布失败,而PUT必须在请求主体包含名称不存在的子实体时失败。这就是为什么我选择使用单endpoint的共享操作。

共有2个答案

梅逸清
2023-03-14

有没有更好的方法来实现这一点?

我想一定有。需要记住的一个想法是:统一接口的要点是,客户机和中介不需要知道任何关于服务器上实现的信息。

GET /people/bob/favoriteColors

200 OK
[]

如果这是我们的起点,我们想在列表中添加一种新颜色

PUT /people/bob/favoriteColors
[ "RED" : { "redChannel":255, "greenChannel":0, "blueChannel":0} ]

200 OK

没问题,我们用PUT“创建”了一种最喜欢的颜色。

PUT /people/bob/favoriteColors
[ "RED" : { "redChannel":239, "greenChannel":0, "blueChannel":0} ,
[ "BLUE" : { "redChannel":16, "greenChannel":16, "blueChannel":239} ]

200 OK

同样,没问题:我们创建了蓝色,更新了红色。请注意,我们故意与服务器在接受此更新时执行的操作隔离开来。

KeyValueStore.put(/people/bob/favoriteColors, [...])

KeyValueStore.put(/people/bob/favoriteColors/RED, {...})
KeyValueStore.put(/people/bob/favoriteColors/BLUE, {...})

KeyValueStore.put(/people/bob, {...,favoriteColors:{...}})

RDBMS.commit( [ favoriteColors.insert(BLUE : {}), favoriteColors.update(RED: {})

这并不是说你的api不允许直接发布到新资源;那也很好

PUT /people/bob/favoriteColors/OCTARINE
{ "redChannel":-inf, "greenChannel":Nan, "blueChannel":i}

201 CREATED

您需要记住的是,从您的沼泽标准、开箱即用、中介组件的角度来看,/People/bob/收藏颜色/People/bob/收藏颜色/OCTARINE之间没有隐含的关系。修改一个不会使另一个缓存项无效——保护我们免受写入实现细节影响的相同接口也保护我们免受对其他资源的副作用。在设计API时,您确实需要考虑拥有多个可以更改“相同”状态的资源的含义。

在某种程度上,你可能有这个问题。中间人不会知道的

DELETE /people/bob

还应驱逐/people/bob/favoriteColors

在到目前为止的所有示例中,我一直在使用寻址资源的完整表示。这就是PUT所能做的——发送目标资源的替换表示。如果您想发送一个变更的表示,那么您需要考虑补丁或POST。

创建POST的耦合是错误的。我的猜测是,耦合是根据RFC 2616中对POST的描述假设的。[RFC 7231]中的语言更像是一个包罗万象的东西。POST是超文本标记语言中唯一支持的写入方法,网络蓬勃发展,所以我们必须能够以某种方式进行管理。

另外一个方法是发送消息,其中所有有趣的工作都是副作用。这类似于向消息队列发送消息。目标资源是队列本身,因此您的请求和响应都对应于将文档添加到集合的概念模型;由于文档本身是要做的工作的表示,而不是结果的表示,因此它们与域模型本身不同。这意味着您可以将beigfycolors命令的完整表示形式(可以任意复杂)发送给调整到特定用例的处理程序,并观察表示形式中的副作用。

这里是命令处理资源,这里是CQRS模式的另一个表达式。与PUT OCTARINE案例一样,中介机构不知道要逐出哪些表示,但在其他情况下,他们可以很好地处理协议。

翟新
2023-03-14

关于正确处理多人关系,让我解释一下,如果我要尽可能坚持ReST原则,我会怎么做。

有几种方法来表达这种关系,一种是您建议使用路径层次结构的方法。您需要以下endpoint:

  • /parents/

这种选择更好地表达了儿童无法独立存在的构图关系。

如果要应用超媒体约束(您应该将其称为API ReSTful),另一个选项是:

  • /parents
  • /parents/:id
  • /childrens
  • /childrens/id

在创建子资源时,您将在请求主体中包含一个指向具有适当rel类型的父资源的链接。例如,如果使用HAL:

{
  ...
  ...,
  "_links": {
    "parent": { "href": "https://api.domain.com/parents/9283jdp92cn"}
  }
}

这种选择更好地表达弱关系或聚合,其中关系的两端可以彼此独立存在。

还有第三种选择,我们应将其视为特例。如果父资源是经过身份验证的主体,则可以隐式地相互关联。例如,如果父对象是用户域实体是照片集合的所有者,则可能会尝试公开以下endpoint:

  • /users

假设只有User只能访问他自己的Photo,这就足够了:

  • /photos
  • /photos/:id

因为经过身份验证的用户将通过安全上下文对endpoint可用,并且可以隐式建立关系,而无需通过层次路径或其他方式显式表示。

从你的问题出发,我发现了一些可能导致不良做法实施的信号。所以这里有一些与你的职位相关的原则,你应该尽可能坚持(务实)。

>

  • 每个资源都需要一个唯一的标识符,即ReST中的URI。正如您已经发现的那样,如果不这样做,在尝试按问题中的设计更新子实体时会产生奇怪的影响。
  • 要ReSTful的API应该实现超媒体约束。如果您的资源ID是URI,您可以在资源之间创建完全合格和丰富的关系,无论它们的服务器位置如何。
  • 标识符应该是不透明的。因此,不要在URI的: id部分使用相关数字或枚举。甚至不要让你的消费者尝试猜测可能会暴露你不想让他们看到的内容的URI。安全是关键,但不透明的身份是额外的。
  • 除非有充分的理由,标识符应该由服务器生成,而不是由客户端生成。否则你的ID不会不透明。
  • 作为经验法则:

    1. 使用POST创建并返回201创建的。我喜欢用资源的主体来回应。并且不要忘记将URI包含到所创建的资源中
    2. 使用GET读取并返回200 OK
    3. 使用PUT修改整个资源。我希望与post保持一致,并以200 OK返回更新的资源
    4. 使用Delete进行删除,并使用204无内容进行响应
    5. 我很少使用补丁进行部分更新

    这可以让你在大多数情况下得到保障。

  •  类似资料:
    • 问题内容: 下面给出从到的一对多关系。 部门(家长): 员工(孩子): 合并如下所示的托管实体(子实体)(在EJB中使用CMT), 不会更新数据库表中的相应员工行。仅当从中的子关系中删除时,才会发生这种情况。 为什么表中的行没有更新?关于此示例的唯一目的是什么? 我当前正在使用具有JPA 2.1的EclipseLink 2.6.0。 问题答案: 级联应该始终从父级传播到子级,而不是相反。 在您的情

    • 我有一个批处理过程,它正在为一组实体重新计算数据。通过Hibernate从DB获取实体列表: 当流程运行时,某些实体似乎正在分离,导致两种症状: 当尝试获取惰性数据时,我得到一个异常: 在我的第一次尝试中,我试图通过调用inside

    • 问题内容: 我正在尝试使用JAXB解组以下XML: 我已经使用XJC生成了所有类。如果要访问Composers集合,则必须执行以下操作: 有什么办法可以代替我吗? 我理解需要一个从XML派生的Composers对象,但是在使用Java进行交易时,拥有一个用于存储集合的中间POJO似乎有点多余。 我的XSD是: 问题答案: 该 @XmlElementWrapper 插件不正是你想要的。

    • 1.【强制】 关于 hashCode 和 equals 的处理,遵循如下规则: 1) 只要重写equals,就必须重写hashCode。 2) 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的 对象必须重写这两个方法。 3) 如果自定义对象做为Map的键,那么必须重写hashCode和equals。 说明: String 重写了 hashCode 和 e

    • 我是Stackoverflow的新手,所以我会尽我所能符合用法。我想知道是否有办法获得给定实体的更改/快照的完整列表。目前,它可以很好地用于单数属性的编辑,以及对集合属性的添加和删除。但我无法找到集合属性中的子实体何时更新。 给定两个实体和一个链接实体: 我的用例如下。我通过ID#1获得特定的人,然后改变特定地址的类型(即HOME- PS:请对代码片段中的任何错误表示歉意,因为我没有在我的开发环境