【问题标题】:Rest Resources Separation休息资源分离
【发布时间】:2021-04-23 00:52:53
【问题描述】:

我一直在尝试使用 Spring Boot 启动一个 REST api,但我对资源的分离以及哪个端点应该在哪个文件中有点挣扎。 假设我们有一个 api enpoint 来处理用户和该用户的成就:

/user/{id} GET - 通过 id 获取用户

/achievement/{id} GET - 按成就获取

它们都在各自的资源文件中:

用户资源

@RestController
public class UserResource {

    public UserResource() {...}

    @GetMapping("/users/{id}")
    public UserDTO getUser(String id) {
        log.debug("REST request to get User : {}", login);
        return userService.getUserWithAuthoritiesById(id).map(AdminUserDTO::new));
    }

和成就资源

@RestController
public class AchievementResource {

   
    public AchievementResource(...) {...}

    @GetMapping("/achievements/{id}")
    public ResponseEntity<Achievement> getAchievement(@PathVariable Long id) {
        return achievementRepository.findById(id);
    }
}

到目前为止一切顺利,非常简单。当我必须从用户那里获得所有成就时,我的问题就来了。命名约定说我应该有一个端点,例如:

/user/{id}/achievements GET

但是这个端点应该在哪里呢?我觉得这两个资源都可能很好,因为对于 UserResource,端点的根是用户,但由于我们正在返回成就,AchievementResource 也可能是合乎逻辑的。

【问题讨论】:

  • 在尝试分离控制器时,确定特定请求所引用的对象非常重要。在您的情况下,您正在寻找特定用户的所有成就。如果是这种情况,我会将这个端点放在 UserResource 类中。如果要查找所有具有特定成就的用户,则该端点会更适合AchievementResource 类。

标签: java spring-boot rest endpoint


【解决方案1】:

简单的回答:你有错误的问题

但是这个端点应该在哪里呢?

资源的定义应该在您机器可读的 api 定义中。通过将定义输入代码生成器以供您选择语言,您可以生成所需的类文件。生成器会将它创建的类放在文件某处中,然后您将它们保留在这种默认排列中,直到将来某个时候,当您有令人信服的理由以不同方式排列它们时(此时,您 fork代码生成器并将您的首选设计设为默认)。


也就是说,在手工设计时,“REST 端点”并没有什么特别之处。资源类所属的准则与 Java 中的任何其他类没有什么不同......

说,我发现有关文件布局启发式的文献相当令人失望。似乎没有很多材料讨论不同设计之间的权衡,或者一种选择可能比另一种更引人注目的环境。

对于您的具体情况,我建议将新资源放入自己的文件中。这里的论点是您的 UserResource 具有 User 依赖项,并且您的 AchievementsResource 具有成就依赖项,但是您的新事物具有 both,并且根据(挥手)原则,我们应该避免带来不需要的成就依赖项进入 UserResource 的命名空间(反之亦然)。

换句话说,如果我们发现自己将导入添加到现有文件以实现新事物,则暗示新事物可能更好地放置在其他地方。

使用单独的文件也有很好的机械优势 - 它减少了合并冲突,每个文件都有自己的源代码控制历史记录(这意味着用户的历史记录不会被一堆专门关于新事物的提交弄乱)。例如,在 CodeScene 上查看 Adam Tornhill 的工作。

【讨论】:

    【解决方案2】:

    你把controller分开了也没有错,你应该按照它们的一般实体来分类方法,“如果我需要恢复用户的成就”,这两者都有关系,但是,她从哪里得到这些数据?的成就知道每个成就在数据库中必须与用户有关系,您可以使用 List returnAchievementsByUser (Integer Id) 方法在成就控制器中很好地查找它。

    【讨论】:

      【解决方案3】:

      这取决于您的观点和幕后业务。在许多情况下,您可以只使用一个端点;如果 "users" 是有成就的主要资源,那么 "/users/{user-id}" 和 {users/{user-id}/achievements/{achievement-id} 通过 Id 和特殊成就获取用户用户

      @RestController
      @RequestMapping("users")
      public class UsersRestController{
      
         @GetMapping("/{user-id}")
          public UserDTO getUser(@PathVariable("user-id") String id) {
              code...
          }
      
         @GetMapping("/{user-id}/achievements/{achievement-id}")
          public AchievementDTO getAchievement(@PathVariable("user-id") String userId, 
                                               @PathVariable("achievement-id") String achievementId) {
              code...
          } 
      

      }

      如果在实体层次结构中将“成就”置于“用户”之上对您和您的业务有意义,那么 /achievements/{achievement-id}/users/{user-id} 可以是一个休息展示:

      @RestController
      @RequestMapping("achievements")
      public class AchievementsRestController{
      
         @GetMapping("/{achievement-id}")
          public UserDTO getAchievement(@PathVariable("achievements-id") String id) {
              code
          }
      
         @GetMapping("/{achievements-id}/users/{user-id}")
          public AchievementDTO getAchievement(@PathVariable("user-id") String userId, 
                                               @PathVariable("achievement-id") String achievementId) {
              code
          } 
      
      
      }
      

      最后,只要它们不在实体层次结构中,您就可以将 userId 传递给 “/achievements/{achievements-id}”(或“/users/{user-id}”的成就ID)作为RequestParam。

      【讨论】:

        猜你喜欢
        • 2014-02-02
        • 1970-01-01
        • 2016-05-01
        • 2014-10-25
        • 1970-01-01
        • 2015-03-06
        • 1970-01-01
        • 1970-01-01
        • 2019-03-17
        相关资源
        最近更新 更多