【问题标题】:Spring REST URI existence checkSpring REST URI 存在检查
【发布时间】:2020-04-08 17:46:56
【问题描述】:

让我们假设一个简单的基于 Spring 的 RESTful API 和一些嵌套资源。

  • User 是每个人创建的用于访问 API 的根实体
  • 每个User 都可以创建Posts,因此Post 没有创建者就无法存在
  • User 也可以评论现有的Posts,因此Comment 属于Post

对于这个 API,我选择了以下 RESTful 路由:

  • /api/usersUser
  • /api/users/{userId}/postsPost
  • /api/users/{userId}/posts/{postId}/commentsComment

关于我的问题,我是否应该验证父资源是否存在?例如对于请求GET /api/users/2/posts/3/comments/7,我是否应该确保实体User(2)Post(3) 也存在?我是否还必须确保实体相互关联,并且没有使用任何 x 存在的任意资源?

示例

@PostMapping("/api/users/{userId}/posts/{postId}/comments")
public void create(@PathVariable("userId") Long userId, @PathVariable("postId") Long postId, @RequestBody CommentPayload payload) {

    // userService.existsByIdWithPostId(userId, postId); ???

    commentService.createForPost(postId, payload);
}

要在数据库中创建评论,userId 完全无关紧要,只需要postId。我是否仍应确保存在userId X 的用户并且有postId Y 的帖子,还是应该忽略它?

如果是,我怎么能优雅地做到这一点?因为像Optional<Comment> findByUserIdAndPostIdAndId(...) 这样不断增长的JPA/Repository 查询似乎不是一个解决方案......然后验证应该在几个查询中进行吗?包含所有父母的 ID 会使实际查询变得非常复杂。此外,标识符必须通过所有层(服务、安全)传递,这使内部 API 变得复杂。

【问题讨论】:

  • @DumiduUdayanga 它们是url参数,不是查询字符串。

标签: java spring spring-boot rest api


【解决方案1】:

简短的回答:是的,您必须检查父/根实体是否存在,是的,您需要定义实体之间的关系

长答案:

没有用户就无法创建帖子这一事实不必通过 URL 结构进行检查。该 URL 仅用于说明 请求什么资源。检查必须直接在代码中完成。检查记录的存在不需要额外的努力和方法,这是因为实体之间的关系。

在为用户添加帖子时,使用/users/{userId}/posts API,需要将帖子添加到id=[userId]的用户的帖子列表中,因此调用相关存储库方法获取用户记录(比如findById),这本身就是用id=[userId]检查用户是否存在的行为。因为如果存储库返回 null,你就知道用户不存在

要设计好的、高效且易于理解的 API,请遵循以下简单规则:

如果资源可以单独访问,它必须有一个基本 URL 作为 /[entity],否则使用父子模式(就像你的帖子一样)。

请注意,此规则并非一直有效,很多时候您需要自定义 API 并将其更改为更高效。

所以,如果一个用户可以看到其他用户的帖子,那么你应该设计这样的 API:

  • /users [对用户实体的请求]
  • /posts [帖子实体请求]

那么对于特定用户的帖子的请求,您应该使用/users/{userId}/posts创建新帖子或阅读它们,对于特定帖子的cmets您应该使用/posts/{postId}/comments

据我了解您的应用程序的设计,您不需要/comments,因为所有 cmets 都与帖子相关,并且应该通过相关帖子访问(如上)。如果 cmets 可以单独访问(例如有一个页面列出所有 cmets 而无需关心其相关帖子),那么您需要/comments API 以获得更多说明

【讨论】:

  • 首先是的 cmets 不能单独访问。其次,您的帖子和用户示例很明显,但是更深一层呢,与我的示例相关的 cmets 呢?
  • @0x1C1B “与我的示例相关的 cmets”是什么意思?
  • /users/{userId}/posts/{postId}/comments/{commentId} 这是不允许的吗?如果是如何处理来验证URI的存在呢?你的例子很明显只是一个嵌套的路由不是问题......但是n个嵌套的路由呢?
  • @0x1C1B 您为 API 指定的任何 URL 都是有效的。按照规则,既然 cmets 不应该被单独访问,那么它们应该被他们的父母访问,即PostUser 不是Comment 的父级,并且由于评论和帖子有关系,因此在添加新评论时检查帖子记录的存在,具有与用户帖子关系相同的场景。所以 cmets 的 `/user/{userId} 是不必要和多余的。当一个实体有多个父或关系时,只需在 DTO 中发送父/关系 ID,而不是在 URL 中
  • 这对我的方案意味着什么?我应该有一个额外的路由/api/posts/{postId}/comments/{commentId} 省略用户前缀吗?
猜你喜欢
  • 2019-07-11
  • 2010-11-21
  • 2012-10-04
  • 2015-04-24
  • 2018-09-07
  • 1970-01-01
  • 1970-01-01
  • 2017-03-31
  • 1970-01-01
相关资源
最近更新 更多