嵌套关系是不是应该反映在 JSON API 的 URL 中?

Posted

技术标签:

【中文标题】嵌套关系是不是应该反映在 JSON API 的 URL 中?【英文标题】:Should nested relationships be reflected in URLs for JSON API?嵌套关系是否应该反映在 JSON API 的 URL 中? 【发布时间】:2018-01-16 09:46:45 【问题描述】:

我正在尝试关注JSON API。我需要公开对嵌套资源的 CRUD 访问:产品评论。

在使用 JSON API 之前,我希望有一个这样的 REST 接口:

GET    /products/:product_id/reviews     - list reviews for a product
POST   /products/:product_id/reviews     - add a review for a product
PATCH  /products/:product_id/reviews/:id - update a review for a product
DELETE /products/:product_id/reviews/:id - delete a review for a product

我看到有人提到像这样的嵌套结构in the spec:

例如,一张照片的 cmets 的 URL 将是:

/photos/1/comments

但我不确定此结构是否适用于所有操作。

一方面,如果我要在评论数据的relationships 下的 POST 正文中指定产品,则创建 POST /products/:product_id/reviews 似乎是多余的。

另一方面,如果在删除评论时指定产品 ID 很有用(也许不是),DELETE /products/:product_id/reviews/:id 似乎是唯一明智的做法; people argue about whether a request body is even allowed for DELETE requests.

我可以嵌套一些请求而不是其他请求:

GET    /products/:product_id/reviews  - list reviews for a product
POST   /products/:product_id/reviews  - add a review for a product
PATCH  /reviews/:id                   - update a review
DELETE /reviews/:id                   - delete a review

但这似乎奇怪地不一致。

我永远无法筑巢:

GET    /reviews     - list reviews for the product specified in params
POST   /reviews     - add a review for the product specified in params
PATCH  /reviews/:id - update a review
DELETE /reviews/:id - delete a review

但这似乎很尴尬,并且似乎与我从文档中引用的第一句话不符。

在使用 JSON API 时,嵌套的资源关系是否应该反映在 URL 中?

【问题讨论】:

【参考方案1】:

我真的很喜欢你的问题,因为我一直有同样的想法。我很困惑,还没有人给出答案。

我已经在生产系统上使用 JSON API 一年多了,我想给我两分钱。

起初,当我开始使用 JSON API 的项目时,我对嵌套资源与非嵌套资源存有疑问。然后我遇到了嵌套资源的问题,而非嵌套资源可以避免这些问题。

要采用与示例中相同的路径,请考虑 GET /products/:product_id/reviews 端点。 当这样做时,将评论嵌套在产品下非常有意义,因为我们最初是在产品的上下文中显示评论。一切都很好。

然后我们想在前端构建一个页面,显示用户以及该用户撰写的所有评论。 虽然我们已经有一个获取评论的端点,但我们将不得不建立一个新的端点,例如GET /users/:id/reviews.

如果我们只是将第一个端点放在GET /reviews 上,并带有?filter[product_id]=:id 的过滤器,我们就可以向该端点添加一个新的过滤器,这在 IMO 中很有意义。

我确实使用嵌套资源,但仅用于像 GET /users/:id/email_settings 这样的单例资源以及其他一些有意义的特殊情况。

根据我的经验,如果每个资源都被认为是独立于其他资源的,那么将来会更容易。存在资源和资源之间的关系。在 API 的上下文中,没有资源“拥有”另一个资源(在业务逻辑的上下文中,这是另一回事)。

我已使用此策略,但在向现有端点添加新功能以及添加新端点时它的效果仍然让我感到惊讶。

【讨论】:

我还想补充一点,这很符合任何特定资源都应该有 1 个规范 URI 的想法。使用嵌套资源,您最终可能会为同一资源找到许多位置,例如/products/1/review/1/reviews/1。一个重要的区别 w.r.t. JSON API 是 relationship resources 似乎没有违反这个想法。它们是两个事物之间关系的规范位置,例如/products/1/relationships/reviews/1 代表这些资源之间的关联。 感谢 Lasse 和 @iamvery 分享您的见解!这是我最终使用的方法。 JSONAPI 希望您能够唯一标识资源,因此如果规范 URL 像 /prod/1/rev/2 一样“嵌套”,则设计暗示 rev 仅存在于 prod 及其 @ 987654333@ 是复合的。规范允许这样做,但构建 id 变得很棘手。否则,如果规范 URL 是 /rev/2 则没问题。这里/prod/1/rev/2 只是描述相关项目的端点,可以按你想要的任何方式进行描述。规范没有规定如何构建 URL。【参考方案2】:

如果你来自 CQRS 阵营,你就会明白为什么设计 Restful API 有时会很尴尬。这很尴尬,因为查询操作(GET)和变异操作(POST、PATCH、DELETE)自然应该使用两种不同的语言。 查询动作自然面向关系且数据丰富;而 Mutation 动作不是。所以使用嵌套的 URL 来遍历关系实体之间感觉很容易。 但是 Mutation 你应该为任务提供足够的信息。有时它像您的 Post 示例一样是多余的。有时会像您的 DELETE 示例一样丢失。有时您的任务涉及许多资源;你不知道该放在哪里。

你应该检查 Facebook Graph API 或 Azure Graph API,他们遇到了同样的问题并且有一些很好的解决方案。您应该遵循一致的设计,这一点很重要。 一些规则是:

删除、更新总是指向资源。 如果您想同时创建对象和主关系,则 POST 与嵌套资源一起使用。次要关系应该放在 BODY 中。如果您有两个相等的关系,请考虑同时拥有两个嵌套 API。 使用 POST 针对虚假资源来处理涉及许多资源的任务。

POST /transferfund

针对不适合任何 HTTP 动词的任务使用 POST 处理虚假关系。例如,您想要删除操作的主体,使用

POST /resource/id/deleteItForMe 原因:“我讨厌它”

【讨论】:

以上是关于嵌套关系是不是应该反映在 JSON API 的 URL 中?的主要内容,如果未能解决你的问题,请参考以下文章

我的 JSON 数据是不是应该包含嵌套字段而不是重复信息?

Spring boot JPA - 没有嵌套对象的 JSON 与 OneToMany 关系

使用 Python 将多个关系表转换为嵌套 JSON 格式

为啥插上otg没反映,连不上u盘

JSON.parse 嵌套 JSON 字符串属性解析

使用 JQuery 解析 JSON 嵌套数组