结构化嵌套RESTAPI

结构化嵌套RESTAPI,rest,spring-boot,Rest,Spring Boot,我正在用SpringBoot编写一个API,试图保持它的restful,但它的结构是嵌套的。所以说我有: /api/examboard/{ebid}/qualification/{qid}/subject/{sid}/module/{mid}/ 我对每个名词都有一个控制器,它会接收所有的Id,问题是我并不真的需要模块的ebid或qid,它们只需要在大多数时候关注主题。它们之间的映射非常简单。一个考试将有许多资格,一个资格将有许多科目等 现在的问题是,假设我采用一种更简单的API设计,我只需要父

我正在用SpringBoot编写一个API,试图保持它的restful,但它的结构是嵌套的。所以说我有:

/api/examboard/{ebid}/qualification/{qid}/subject/{sid}/module/{mid}/
我对每个名词都有一个控制器,它会接收所有的Id,问题是我并不真的需要模块的ebid或qid,它们只需要在大多数时候关注主题。它们之间的映射非常简单。一个考试将有许多资格,一个资格将有许多科目等

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

api/subject/{sid}/module 

然后,我需要根据JPA的工作方式在控制器中包含多个服务。因为我需要包括基于
SubjectEntity
的调用和基于
ModuleEntity
的调用。但是,我希望在控制器/服务和服务/存储库之间保持一对一的关系。这就是为什么我选择了上面提到的较长的url,但这似乎有点过头了。有人对我应该如何构造这样一个API有什么建议吗?大多数示例都很小,不太合适。

我将只讨论您的REST API结构问题

正如你已经指出的那样

这方面的问题是,我并不真正需要模块的ebid或qid,它们只需要在大多数时间关注主题

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

否则,作为多个关系一部分的每个实体也应该通过关系另一端的子资源进行访问。 据我所知,您在
examboard
qualification
之间有一种单一的关系,因此我们得到:

api/examboards/{eid}/qualifications
api/qualifications/{qid}/examboard
Yo还可以删除
examboard
子资源,并将其包含在
限定
响应中

对于许多不动产关系,您需要两个子资源:

api/foos/{fid}/bars
api/bars/{bid}/foos
还有另一种资源来操纵关系本身

api/foosToBars/{fid}+{bid}

或者类似地。

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

首先,“视情况而定”。我知道,但确实如此。设计API的方式在很大程度上取决于将定义所需访问模式的用例。你经常需要一门学科的所有模块吗?然后介绍
/subjects/{sid}/modules
,如果您需要examboard中资格认证中的某个主题模块的详细信息,请务必提供
/examboards/{ebid}/qualifications/{qid}/subjects/{sid}/modules/{mid}

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

检索examboard的资格

  • GET/examboards/{ebid}/qualifications
    简单明了
  • GET/qualifications?ebid={ebid}
    如果您觉得以后可能需要复杂的筛选
  • 或者为examboard创建一个新的限定符

  • POST/examboards/{ebid}/qualifications
    ,详细信息在正文中提交
  • POST/qualifications
    在正文中提交详细信息,并将相关的examboard
    ebid
    作为提交数据的一部分
  • 或更新现有资格

  • PUT/qualifications/{qid}
    (如果此操作是幂等的)
  • POST/qualifications/{qid}
    (如果不应将其视为幂等)
  • 或删除资格

  • DELETE/qualifications/{qid}
    删除实体,级联删除关联
  • DELETE/examboard/{ebid}/qualifications
    清除examboard中的所有资格,而不实际删除资格实体
  • 当然有更多的方法让API完成所有这些事情,但这应该表明您需要首先考虑您的用例,并围绕它们设计API

    请注意前面示例中收集资源的多元化。这可以归结为个人偏好,但我倾向于遵循SamRuby在RESTfulWeb服务中的观点,即集合应该是API中的一流公民


    通常,控制器、服务和存储库之间不应该有1:1:1关系的理由。通常,这甚至是不可能的。现在,我不知道您为什么要这样做,但是遵循这一点将迫使您在数据库查询和模型中加入大量逻辑。虽然这(取决于您的设置和技能)可能很容易测试,也可能不容易测试,但它肯定会将所需的测试类型从单元测试(更简单、通常更快、更细粒度)转移到集成测试(需要更多设置、更复杂、通常更慢),当您的服务中没有大部分业务逻辑时,您会将它们放入存储库中的许多联接和子选择中。

    Ya我发现自己已经依赖实体中的一个omany映射,我想我不必在几乎所有情况下都懒散地获取其他实体。Ya我将改为:api/foos/{fid}/bar api/bar/{bid}/foos。我认为它更容易让人阅读,也更易于使用。