【问题标题】:Structuring nested rest API构建嵌套的 rest API
【发布时间】:2015-09-13 10:11:03
【问题描述】:

我正在用 Spring Boot 编写一个 API,试图让它保持安静,但结构非常嵌套。所以说我有:

/api/examboard/{ebid}/qualification/{qid}/subject/{sid}/module/{mid}/

我对每个名词都有一个控制器,它可以接收所有 Id,问题是我真的不需要模块的 ebid 或 qid,他们只需要在大多数时间关心主题.它们之间的映射非常简单。一个考试板会有很多资格,一个资格会有很多科目等等......

现在的问题是,我需要一个更简单的 API 设计,我只需要父 ID,因此主题控制器也将具有:

api/subject/{sid}/module 

然后我需要根据 JPA 的工作方式在我的控制器中包含多个服务。因为我需要包括基于SubjectEntity 的调用和基于ModuleEntity 的调用。但是,我想在我的控制器/服务和服务/存储库之间保持一对一的关系。这就是我选择上面提到的更长网址的原因,但这似乎有点矫枉过正。有没有人对我应该如何构建这样的 API 有任何建议,大多数示例都很小而且不太适合。

【问题讨论】:

    标签: rest spring-boot


    【解决方案1】:

    如果不了解更多关于您的模型以及它们之间的关系,这个答案将不得不保持有点分散。

    首先 - “这取决于”。我知道,但确实如此。设计 API 的方式很大程度上取决于定义所需访问模式的用例。您是否经常需要一个主题的所有模块?然后介绍/subjects/{sid}/modules,如果您需要考试板资格认证中某个主题模块的详细信息 - 一定要有/examboards/{ebid}/qualifications/{qid}/subjects/{sid}/modules/{mid}

    正如您所说,您的实体之间存在许多关系。这很好,但这并不意味着您需要您的 API 在专用端点中捕获这些关系中的每一个。您应该在此处区分检索和修改实体。在下面找到您可能想要的某些操作的示例(不知道您的模型,这可能不适用 - 让我们将其视为示例)

    检索考试委员会的资格

    1. GET /examboards/{ebid}/qualifications简单明了
    2. GET /qualifications?ebid={ebid} 如果您觉得以后可能需要复杂的过滤

    或为考试板创建新的资格

    1. POST /examboards/{ebid}/qualifications 在正文中提交详细信息
    2. POST /qualifications 在正文中提交详细信息,并将关联的考试板 ebid 作为提交数据的一部分

    或更新现有资格

    1. PUT /qualifications/{qid}(如果这个操作是幂等的)
    2. POST /qualifications/{qid}(如果它不应该被认为是幂等的)

    或删除资格

    1. DELETE /qualifications/{qid} 删除实体,级联删除关联
    2. DELETE /examboards/{ebid}/qualifications 清除考试板中的所有资格,而不实际删除资格实体

    当然还有更多的方法可以让 API 完成所有这些事情,但这应该表明您需要首先考虑您的用例并围绕它们设计您的 API。

    请注意前面示例中馆藏资源的多元化。这取决于个人喜好,但我倾向于遵循 Sam Ruby 在 RESTful Web 服务 (available as PDF) 中的论点,即集合应该是 API 中的一等公民

    通常情况下,控制器、服务和存储库之间的关系应为 1:1:1。通常,这甚至是不可能的。现在,我不知道您可能想要这样做的原因,但坚持下去将迫使您将大量逻辑放入数据库查询和模型中。虽然这(取决于您的设置和技能)可能容易测试,也可能不容易测试,但它肯定会将所需的测试类型从单元(更简单,通常更快,更细粒度)转移到集成测试(需要更多设置,更复杂,通常较慢),而不是将大部分业务逻辑放在服务中,而是将它们放入存储库中的许多连接和子选择中。

    【讨论】:

    • 是的,我已经发现自己依赖于实体中的 OneToMany 映射,我想我宁愿不必在几乎所有情况下都懒惰地获取其他实体。
    【解决方案2】:

    我只会解决您的 REST API 结构问题。

    正如你已经指出的那样

    问题在于我真的不需要模块的 ebid 或 qid,它们只需要在大多数时候真正关心主题

    如果您的实体可以代表自己,则需要将您的实体视为资源,并为其提供自己的顶级资源。相反,如果您的实体仅作为另一个实体的一部分存在,则在其父实体下方构建一个子资源。这应该与您的对象模型设计中的关联类型聚合和组合相对应。

    否则,作为多关系一部分的每个实体也应该可以通过关系另一端的子资源访问。 据我了解,examboard 和 qualification 之间存在 OneToMany 关系,因此我们得到:

    api/examboards/{eid}/qualifications
    api/qualifications/{qid}/examboard
    

    你还可以删除 examboard 子资源并将其包含在 qualification 响应中。

    对于多对多关系,您需要两个子资源:

    api/foos/{fid}/bars
    api/bars/{bid}/foos
    

    以及操纵关系本身的另一种资源。

    api/foosToBars/{fid}+{bid}
    

    或者类似。

    【讨论】:

    • 是的,我将回到:api/foos/{fid}/bars api/bars/{bid}/foos。我认为它更易于阅读且易于使用。
    猜你喜欢
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多