REST API设计最佳做法

时间:2020-09-03 13:24:13

标签: java spring rest

此端点属于什么地方,应该如何:

GET /users/1/comments/

GET /comments&userId=1

UserController我在哪里

@GetMapping("/user/{userId}/comments/")
    public ResponseEntity<List<Comment>> getAllComments(@PathVariable Long userId) {
        return ResourceUtil.getResponseEntity(Optional.of(userService.getUser(userId).getComments()));
    }

或者应该在CommentController中:

@GetMapping("/comments/")
    public ResponseEntity<List<Comment>> getAllComments(@RequestParam Long userId) {
      User user = userService.getUser(userId);
      List<Comment> comments = commentService.getComments(user);
      return ResourceUtil.getResponseEntity(comments);
    }

如果我选择遵循此约定:

GET /users/1/comments/,最后我只有一个控制器-所有资源的UserController,因为所有其他资源都可以看作是/users的子资源。例如:

GET /users/1/articles/ GET /users/1/jobs/ GET /users/1/applications/ GET /users/1/posts/

以此类推...

/users/映射到不同的控制器是一个好主意,因此在这种情况下:

GET /users/1/articles/-> ArticlesController

GET /users/1/jobs/-> JobsController

GET /users/1/applications/-> ApplicationsController

GET /users/1/posts/-> PostsController

更新

我认为这是不正确的:

GET /users/1/comments/,因为对于任何给定的用户实体,我们最终都将给他们前缀相同的值/users/1,这是错误的。身份验证和授权应该发生得很顺利,因此,控制器应该能够从请求标头/ JWT令牌中提取用户信息,并且它们不应该是API的一部分,除非它是管理员端点,并且管理员可以查看任何用户,但是该端点应该完全分开。

6 个答案:

答案 0 :(得分:1)

正确的方法是
/users/1/comments/

所有资源均应通过URL路径(而非查询参数)标识。 只有不是资源标识符的数据才应该是查询参数
/users/1/comments?startsWith=mark&page=3

答案 1 :(得分:1)

您提供的每个版本都可以“正确”;他们的意思略有不同。

在第一种情况下,您将comments视为用户的子资源;也就是说,这些评论在其外部没有任何有意义的存在。如果您要表示用户的身份验证信息或首选项,则完全使用此样式。这种样式等效于使用SQL JOIN,并且可以在数据库中可能会使用级联删除的地方使用它。

在第二种情况下,您将comments视为一流的顶级资源,它独立存在,并且与用户之间没有关系。查询参数与WHERE子句的HTTP等效。

comments视为顶级资源还是子资源是您要做出的设计决定。支持顶级资源的因素包括:始终以各种不同的关系进行查询(例如,以?article=进行查询比以?user=进行查询!)以及解决某个问题是否有意义通过ID进行评论,而无需引用发布用户。

答案 2 :(得分:1)

REST不在乎您将标识符用作什么拼写,只要它们符合RFC 3986定义的生产规则

GET /users/1/comments/
GET /comments&userId=1
GET /79f541f3-e872-47ba-9ef6-63667e148455

这些都是 fine

部分要点是标识符只是标识符;就通用组件而言,标识符在语义上是不透明的,因此您可以对它们进行所需的操作。

在某些情况下,您可能想利用relative references的优势,它允许您操纵标识符的路径段(也称为“分层部分”)来轻松计算其他标识符。

例如,可能很方便

/users/1 == base(/users/1/comments/).resolve(../..)

如果希望使用RequestParam映射,则需要确保这些键值对出现在URI的查询部分中。换句话说,这两个拼写都是 fine (从通用客户的角度来看)

GET /comments&userId=1
GET /comments?userId=1

但是,在实现路由时,第二个将比第一个更容易使用。

键/谷值对的另一个优势是HTML语义包括表单处理规则,该规则可以理解如何将数据和元数据从输入控件转换为应用程序的查询部分中的application / x-www-form-urlencoded键值对。标识符。

请记住,REST API是外观。您的实现伪装成一个哑网站,因此您可以利用任何和所有现有的通用组件来了解我们如何与哑网站交谈。在网站上,URI只是一个不透明的键,我们用它来将文档从商店中拉出(与使用字符串从java.util.Map<String,Value>中提取值的方式几乎相同)。

答案 3 :(得分:1)

我没有答案,因为这确实取决于您的情况。

考虑一下:没有用户的评论是否可以存在?如果评论与某个产品相关,那么谁拥有评论,用户或产品?如果您没有答案,您应该问:评论对哪个“类别”更为重要?如果您失去了用户,应该失去评论吗?

在设计API时,我总是问这些问题。希望能有所帮助。

答案 4 :(得分:0)

按照约定,对于每个资源,您都需要一个控制器。这是 Richardson成熟度模型的第1级。

所以最好的解决方案是数字3。但是我认为userId将是注释表中的外键,因此您可以直接使用它而无需获取数据库的用户。

您可以在 Richardson成熟度模型中看到此文章: Richardson Maturity Model

答案 5 :(得分:0)

@PathVariable@RequestParam均用于提取值。

它们的用途不同。这些是:

    尝试获取唯一值时,使用
  • @PathVariable注释。

示例:GET /getMessages/1

  • @RequestParam批注用于对值进行过滤和排序。

示例:GET /search?field1=abc&field2=cdf

以您为例,在尝试获取唯一的收集资源时,应使用GET /users/1/comments/

相关问题