RestController 设计困境 – Spring Boot REST API

huangapple go评论46阅读模式
英文:

RestController design struggle - Spring Boot REST API

问题

我是REST API开发的新手。我决定使用Spring Boot创建一个博客应用程序,但我在应用程序的设计和结构方面遇到了困难。

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

现在,我正在考虑如何设计@RestControler以处理帖子(Posts)。在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. 是否可以在PostController中添加一个新的方法addComment,用于创建新的评论?

在我的想法中,将评论添加到帖子中似乎应该属于帖子,但我真的不确定。

有人能给我一些关于这个问题的建议吗?

非常感谢!

再见,
Tom

英文:

I'm quite new into REST API development. I have decided to create a Blog application using Spring Boot and I'm really struggling with the design and structure of my app.

Right now my app consists of Post and Comment models and repositories. For both models, I have created service classes (PostService and CommentService). In these classes, I have all the business logic (just simple CRUD right now).

Now I am scratching my head about the design of my @RestControler for Posts. In PostController I have exposed these actions:

@PostMapping(&quot;/api/posts/create&quot;)
public Post create(@RequestBody Post post) { ... }

@GetMapping(&quot;/api/posts&quot;)
public List&lt;Post&gt; findAll() { ... }

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

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

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

Now I'm getting to my question. I am wondering what is correct design of adding a Comment to the Post.

  1. Should I expose all CRUD method for Comment using CommentController class and use create method?
  2. Is it ok to add a new method addComment to PostController which will create a new Comment?

In my head adding a Comment to the Post belongs to the Post, but I really don't know.

Could some of give me some advice regarding this matter?

Thanks a lot!

Bye,
Tom

答案1

得分: 1

如果我是你,我会考虑来自OpenAPI规范的REST设计原则,并遵循资源 -> 子资源 -> 方法||标识符模式。这可能是最符合KISS原则和清晰设计的方式,以提高可读性和理解性。

@PostMapping("/api/posts/") //你不需要将/create作为单独的URI
public Post create(@RequestBody Post post) { ... }

@GetMapping("/api/posts") //这是可以的。
public List<Post> findAll() { ... }

@GetMapping("/api/posts/{id}") //可以,但{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) { ... }

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

@GetMapping("/api/posts/{id}/comments/{commentId}") //commentId是可选的
@PostMapping("/api/posts/{id}/comments/") //这里不需要{commentId},只需发布有效载荷

你还可以在这里查看RESTful命名约定。

英文:

If I were you, I'd consider REST Design Principles from the OpenAPI Specification and would follow resource -&gt; sub-resource -&gt; method||identifier pattern. This would probably be the most KISS and clean design for the readability and understanding purposes.

@PostMapping(&quot;/api/posts/&quot;) //you don&#39;t need /create as a separate URI
public Post create(@RequestBody Post post) { ... }

@GetMapping(&quot;/api/posts&quot;) //This is OK.
public List&lt;Post&gt; findAll() { ... }

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

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

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

and now, for the comments API design, I would have contain them under posts resource, and would have added these corresponding URIs:

@GetMapping(&quot;/api/posts/{id}/comments/{commendId}&quot;) //commentId is optional
@PostMapping(&quot;/api/posts/{id}/comments/&quot;) //you don&#39;t need any {commendId} here, just post the payload

and etc. I hope you can come up with method signatures and other method mappings.

You can also see the RESTful naming conventions here

答案2

得分: 0

坦白说,我认为没有人能在这里给出完美的答案。这通常是一个个人决定。通常情况下,你可以关于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的评论后,我认为另一种好的方式是在CommentsController中使用这个URL @PostMapping("/api/posts/{id}/comment")。我认为这是一个不错的方式。

英文:

To be honest I don't think that someone can give you the perfect answer here. It is often a personal decision. In common you can say the following about a REST API.

  • the path should only represent your data structure in the database. So for example /api/posts

  • No verbs in your path. What you want to do should be handled by the RequestType (GET, POST, PUT, PATCH, DELETE, etc.)

Now to your case. I can really good understand why you are struggling. I think here are two options:

  1. PostsController

    You say a Comment is always a part of a Post and because of this
    you design your API like this.

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

    You handle Comment as an own object and the Post is just a relation you add to it by attribute.

    @PostMapping(&quot;/api/comments&quot;)
    public Comment create(@RequestBody Comment comment) { ... }
    

So it is always is it a Subset vs make own Object structure. I think in this case here I would prefer option 2 because I think you want do more operations on this object.

Also you can than design your API in the way that every Controller starts with the object that will be handled /api/OBJECT/xxx/yyy

UPDATE

After reading the comment from @gulliva I think also a good way is to use this URL @PostMapping(&quot;/api/posts/{id}/comment&quot;) but put it in the CommentsController. I think this is a good way.

huangapple
  • 本文由 发表于 2020年7月30日 15:25:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/63168162.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定