RestController设计难点 - Spring Boot REST API

3

我对REST API开发比较陌生。我决定使用Spring Boot创建一个博客应用程序,但我的应用程序的设计和结构一直困扰着我。

目前,我的应用程序包括文章和评论模型和存储库。对于这两个模型,我创建了服务类(PostService和CommentService)。在这些类中,我有所有的业务逻辑(目前只是简单的CRUD操作)。

现在,我正在考虑如何设计@RestControler以处理文章。在PostController中,我公开了以下操作:

@PostMapping("/api/posts/create")
public Post create(@RequestBody Post post) { ... }

@GetMapping("/api/posts")
public List<Post> findAll() { ... }

@GetMapping("/api/posts/{id}")
public Post findById(@PathVariable("id") Long id) { ... }

@PutMapping("/api/posts/{id}")
public Post update(@RequestBody Post post) { ... }

@DeleteMapping("/api/posts/{id}")
public void delete(@PathVariable Long id) { ... }

现在我要开始提问了。我想知道,添加评论到帖子的正确设计是什么。

  1. 我应该使用CommentController类公开所有CRUD方法并使用create方法来添加评论吗?
  2. 将一个新的方法addComment添加到PostController中来创建一个新的评论是否可以?

在我的脑海中,将评论添加到帖子属于帖子,但我真的不确定。

能否给我一些关于这个问题的建议呢?

非常感谢!

再见, Tom


1
你的API看起来还不错!你可以从/api/posts/create中删除create,因为POST请求仍然意味着你要创建一个帖子。如果要添加评论,你可以添加一个CommentController,它依赖于你的两个服务(PostService和CommentService)。URL可能看起来像这样/api/posts/{id}/comment。请参考https://spring.io/guides/tutorials/rest/。 - Gulliva
创建一个新的控制器来处理评论功能。将它们保留在同一个控制器中会使该控制器变得笨重,因为您最终需要添加其他评论部分的功能。 - k9yosh
1
谢谢大家……只有一个问题。既然没有帖子就没有评论,那么在CommentController中使用像这样的URL @PostMapping("/api/post/{id}/commets") 来创建方法是否正确? - kovalensue
2个回答

1

说实话,我认为没有人能在这里给你完美的答案。通常这是个人决定。关于REST API,您可以说以下内容。

  • 路径应该只表示数据库中的数据结构。例如/api/posts

  • 路径中没有动词。您想要做什么应该由请求类型(GET、POST、PUT、PATCH、DELETE等)处理

现在针对您的情况,我可以非常好地理解您为什么感到困难。我认为有两个选择:

  1. PostsController

    你说一个 Comment 总是属于一个 Post,因此你设计了这样的 API。

    @PostMapping("/api/posts/{id}/comment")
    public Comment create(@PathVariable Long id), @RequestBody Comment comment) { ... }
    
  2. CommentsController

    你将 Comment 处理为一个独立的对象,而 Post 只是你通过属性添加的关系。

    @PostMapping("/api/comments")
    public Comment create(@RequestBody Comment comment) { ... }
    

因此,它总是子集 vs 创建自己的对象结构。在这种情况下,我认为我更喜欢选项2,因为我认为您想对此对象执行更多操作。

此外,您可以设计您的API,使每个控制器都以将要处理的对象开头/api/OBJECT/xxx/yyy

更新

阅读@gulliva的评论后,我认为另一种好方法是使用此URL@PostMapping("/api/posts/{id}/comment"),但将其放在CommentsController中。我认为这是一个好方法。


1
如果我是你,我会考虑来自OpenAPI规范的REST设计原则,并遵循资源 -> 子资源 -> 方法||标识符的模式。这可能是最简单和清晰的设计,以提高可读性和理解性。
@PostMapping("/api/posts/") //you don't need /create as a separate URI
public Post create(@RequestBody Post post) { ... }

@GetMapping("/api/posts") //This is OK.
public List<Post> findAll() { ... }

@GetMapping("/api/posts/{id}") //OK, however {id} should be optional, hence you can combine this and upper methods in one method.
public Post findById(@PathVariable("id") Long id) { ... }

@PutMapping("/api/posts/{id}") //OK.
public Post update(@RequestBody Post post) { ... }

@DeleteMapping("/api/posts/{id}") //OK.
public void delete(@PathVariable Long id) { ... }

现在,对于评论API设计,我会将它们包含在帖子资源下,并添加相应的URI:

@GetMapping("/api/posts/{id}/comments/{commendId}") //commentId is optional
@PostMapping("/api/posts/{id}/comments/") //you don't need any {commendId} here, just post the payload

我希望你能提供方法签名和其他方法映射。等等。

您还可以查看RESTful命名约定 此处


非常感谢您提供的优秀答案。您能否解释一下为什么要将评论方法放在PostController中呢? - kovalensue
1
@TomášNa'viKoválik 不是方法,而是在帖子URI下的资源路径URI。从表示层面考虑。评论没有自己的父资源“Post”,对吧?因此,单独处理评论不会是将API公开给第三方世界时直观和可理解的设计。此外,如果您为GET和POST创建两个离散的URI(如接受的答案建议),这也将违反DRY原则...并且您只会有重复,这是不好的。 - Giorgi Tsiklauri
好的,现在我更明白了。还有一个问题:如果将来我决定不仅为帖子使用注释,那么我应该为注释公开单独的API,对吗? - kovalensue
1
首先,在编码之前,你应该好好思考你的设计。 :) 你的评论是什么?为什么要这样做?在哪里使用它们?.. 首先,你必须想出你想要实现的抽象设计,然后.. 这取决于你认为你的评论将是什么。在你目前的例子中,很明显,你有一个帖子,还有它们各自的评论。因此,就像没有人没有PID一样,如果你没有父级帖子(例如,删除帖子),评论也不会存在。 - Giorgi Tsiklauri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接