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

Should nested relationships be reflected in URLs for JSON API?

我正在尝试关注 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:

For example, the URL for a photo’s comments will be:

/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

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

使用JSONAPI时是否应该在URL中体现嵌套资源关系?

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

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

起初当我开始将要使用 JSON API 的项目时,我对嵌套资源与非嵌套资源存在疑问。然后我 运行 解决了嵌套资源的问题,而这些问题本可以通过非嵌套资源避免。

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

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

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

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

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

我已经使用过这个策略,它在向现有端点添加新功能和添加新端点时的效果仍然让我感到惊讶。

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

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

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

    POST /transferfund

  • 使用 POST 来对抗虚假关系,因为任务不适合任何 HTTP 动词。例如,你想要删除操作的正文,使用

    POST /resource/id/deleteItForMe { reason: "I hate it"}